How to organize custom waitings in your C# tests

Every test automation specialist may encounter a situation when he needs to wait for something in his automated tests. For example, if he is testing a desktop application, he might need to wait for its GUI to be loaded completely. Or if he is designing automated tests for a web application, he might need to wait for some elements to be displayed on a page, to appear in the DOM, to be checked, etc. In some cases, test frameworks provide their own “smart” ways of waiting for events or conditions to happen (like Selenium WebDriver), however lots of tests that I’ve seen in my life still contain “Thread.Sleep”-like delays with hard-coded timeout values. Today I’m  gonna talk why it’s not a good practice and how to do it in a smarter way.

Remember that well-designed code (test code also) doesn’t need Thread.Sleep expressions. However, sometimes it’s unavoidable…Important note!

Let’s start with an example! Imagine that we need to test uploading a movie to the server. A typical movie is represented by some arbitrary Movie class which has 2 methods: UploadToServer() and GetUploadStatus() methods. As you can see from the methods’ names, they do what they say they do: the first method just initiates uploading a movie file to the server and the second method returns a status of uploading (done or not). Pretty simple situation and pretty simple design, aren’t they?

A simple test for testing the above-mentioned feature may look like:

A couple of comments for the code above. Right after we start uploading a file, there is the Thread.Sleep expression which stops our current test execution for some time (60 seconds in our case). It’s necessary because we don’t exactly know how much time is needed for completing uploading our movie to the server. It may vary and it will vary, however, 60 seconds is our max time requirement for the uploading feature. After 60 seconds of waiting, we assert the uploading status and finish our test. Looks good but what are the disadvantages of using the Thread.Sleep expression as it is?

There are several drawbacks:

  • such test will take 60+ seconds in any case (even if a movie is uploaded in 5 seconds). It might not be an issue if you have only one test, but what if you have 10, 100, 500 or even more tests? It will lead to time-consuming test runs and performance issues;
  • using a Thread.Sleep pauses our main thread and that means that tests or application which are running in the same thread cannot interact with us and the system;
  • we have to put a Thread.Sleep for each line in the code where we need to wait, provide specific delay values and maintains this code painfully and properly.

Let’s create the separate Helper class and then create the static WaitForResult() method. What we’re gonna do in this method is to wait for some condition to become true and then stop waiting, if a timeout is expired. In order to make the method more generic and flexible, we can pass a Func<bool> delegate as one of the parameters. Such delegate represents a method that has no parameters and returns a boolean value (true or false), so it covers our needs very well.

But what about the timeout? What is the best way to provide it for the method in order to solve all required challenges? The answer is inside the System.Threading namespace, namely in the CancellationToken structure. The main purpose of this structure is to provide notifications that an operation should be canceled. Let’s see now how the draft of our method looks like:

Inside the method’s body we should provide several steps:

  1. Organize continuous checking our condition (following our example, to check, if our movie has been uploaded or not).
  2. Provide exiting from the method, if we reach the timeout specified.
  3. Configure how often we will check our condition again if it’s not met and the timeout hasn’t expired yet.

The possible solution is displayed below:

The logic of our method is concentrated inside the while loop – it’s executed only in case if our condition isn’t met. Then we provide some delay which can be specified by the PollingRate public property. As you noticed, the PollingRate object is the object of the TimeSpan class, i.e. we can easily assign any delay (milliseconds, seconds, minutes or even hours). The less delay we put, the more accurate our WaitForResult() method will be. But be careful – if you put a very short delay, it may affect the performance of your system.

After the delay, we check if our timeout is expired by accessing the IsCancellationRequested property of the timeout object. It becomes true only in case if the timeout value passed to the WaitForResult() method is up. If it’s true, it means that we couldn’t wait for our main condition to become true, we should fail the case and leave the method. I just decided to throw the TimeoutException but you can easily implement your own logic, for instance, you can return false and then handle it later on in the assertion of your test.

Let’s now create another test with our brand new WaitForResult() method:

There are a couple of interesting places in the test above. The first one is the usage of the WaitForResult() method. As you can see, we use lambda syntax where we pass no parameters to the Func<bool> delegate (as designed) and then specify the timeout. Another moment to pay attention to is the declaring the timeout itself. We can do it inside the test method, if it’s unique for each test, or move on top to the private members of the test class, if it’s the same for all tests, or even move its initialization to the [SetUp] / [OneTimeSetUp] methods.

And the final example I’d like to show is related to the Selenium WebDriver. Let’s pretend that we need to test the following scenario: open the GitHub main page, go to the “Features” menu and wait for the “Code Hosting” icon to be displayed on the page. The test below contains all the steps described but don’t judge me for the hard-coded locators, patterns, and some initialization logic absence – it’s just an example, so I left a lot of code behind:

As you can see, everything looks similar and we can use the WaitForResult() method without any internal changes just by passing a proper condition to check. I also added one extra line of code for assigning the Helper.PollingRate property, just to show you that you can redefine the polling frequency in any place of your test code.

Like this post?

Subscribe to updates from my blog, if you don't want to miss more interesting future posts and materials

Please check your email and confirm subscription

Pin It on Pinterest

Share This