Acceptance tests for GUI are commonly regarded as painful, mainly caused by two factors:
- They have a long feedback loop
- Their stability is poor
A common stability issue is dealing with asynchronous requests. Because ajax calls takes an unknown amount of time to complete, the test might do some assertion based on an incomplete page load (asynchronous requestes haven't finished), but when run again the page might be finished loading before the assertion and the test is ok. This makes the tests unpredictable.
In this blog post I will show you one way of making Selenium wait for AngularJS before proceeding to the next test step.
Make AngularJS keep track of requests using an HTTP interceptor
AngularJS needs to keep track of it's HTTP requests. This must be done on a global level inside the AngularJS app, across the different services.
This can be done using an HTTP interceptor (AngularJS docs). The interceptor "hijacks" the HTTP requests and updates a counter (pendingHttpRequests) on the root scope.
##### Using TypeScript ##### Accessing pendingHttpRequests The counter is available on the root scope and can be accessed on the front page inside the ng-app directive (you would probably not include this markup in the production environment): ` ` ##### Make Selenium wait for pending requests Now that there's an element in the DOM containing the number of active HTTP requests, Selenium needs to check and wait for this number to become 0 before proceeding with each step (The [AfterStep] attribute is used to make the logic run after each test step)This code will run until the pending HTTP requests are 0. In real life you should consider adding a safety timeout to the while loop.
The Thread.Sleep before the while loop is to avoid Selenium checking the DOM value before the ajax request is sent.
##### Conclusion I have shared my implementation for:- Keeping track of ajax calls by using an HTTP interceptor (AngularJS/TypeScript)
- Adding the pending-HTTP-requests-counter to the DOM
- How Selenium utilizes the counter to wait for pending requests to finish before proceeding to the next test step.
I have used this implementation in several projects with success, and hopefully this can be useful to others.
PS: An alternative to this solution might be using the ngWebDriver (I haven't tested this).