Test Automation: How to wait for attribute to change (C#)

Hi all,

I was looking at my code from Day 21 of the 30 Days of Tools challenge. This involved clicking the button on the following website: Click the Button - Ministry of Testing

In my code, I used GetAttribute(“src”) to check if the src attribute contained the words red or green to indicate the current colour of the button. Because it took about 5 seconds for the button to change back from red to green, I added a 5 second wait until the test checked the colour again.

        Assert.True(button.GetAttribute("src").Contains("green"));
        button.Click();
        Assert.True(button.GetAttribute("src").Contains("red"));
        Thread.Sleep(4000);
        Assert.True(button.GetAttribute("src").Contains("green"));

It was pointed out to me that this was inefficient so I was looking to see if I could adjust the code so it waited until the colour changed rather than just waited 5 seconds. I’m struggling to find a way to make it so the test waits for an attribute to change. Just wondering if anyone has any ideas how to wait for an element attribute to change to a certain value?

The best I’ve come up with so far is to have 2 separate elements, 1 for the button when its red and another for the button when its green and then wait for the green button to exist.

    [OneTimeSetUp]
    public void Setup()
    {
        Driver = new ChromeDriver(Environment.CurrentDirectory);
        Driver.Navigate().GoToUrl("https://ministryoftesting.github.io/the-button/#");
        Driver.Manage().Window.Maximize();
    }

    [OneTimeTearDown]
    public void Teardown()
    {
        Driver.Quit();
    }


    [TestCase]
    public void CheckButtonWorksAsExpected()
    {
        IWebElement button = Driver.FindElement(By.Id("buttonImage"));
        By greenButton = By.XPath("//img[contains(@src, 'green')]");
        By redButton = By.XPath("//img[contains(@src, 'red')]");

        Assert.True(Driver.FindElement(greenButton).Displayed);

        button.Click();
        Assert.True(Driver.FindElement(redButton).Displayed);

        WebDriverWait wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(10));
        wait.Until(ExpectedConditions.ElementExists(greenButton));
    }
3 Likes

Have never looked at C# bindings onto Selenium, not looked at C# in fact for about 5 years now… but in the python scripting language I use a wait_until() helper which accepts a lambda, which is merely polled until it returns true or a timeout occurs.

def wait_until(condition, timeout=None, timeout_msg=None, poll_rate=0.1, 
               raise_on_timeout=True, is_not_none=False):

is a lambda/funtion object That last arg <is_not_none> is a python null data type hack, ignore it for now. This code is from 2 layers down in our framework, so tests call it via a wrapper, but this works pretty well. Why? Well the selenium framework is going to be polling the DOM anyway, regardless of how you try and set up any kind of events. So that’s a load of traffic over the wire, and polling fast like we do here becomes less of an issue suddenly. BUT!

What you have there, but only lacking some builder/syntactic sugar is totally more “selenium-esque” than my wait_until hack, because it will allow 3rd party selenium wrappers to optimize the calls on the wire (I dont know if any of the cloud labs do this kind of optimize? I want @restertest to tell us more if possible.

1 Like