In this article, I will show how to use Async libarary in Cucumber protractor tests when working Cucumber data table.
What is Async Library?
If you have seen Protractor APIs, most of the APIs are asynchronous. Hence we need Async like libraries to work efficiently.
Cucumber Data Table:
Data Table comes handy when you want to pass combinations of inputs to your step definitions
Given the following users exist:
| name | email |
| Scott | email@example.com |
| John | firstname.lastname@example.org |
| Steve | email@example.com |
Understanding the problem when using Cucumber Data table without Async :
Assume, you want to test some objects exists on the page. You may want to pass data table in Cucumber scenario to assert multiple objects.
Suppose, I want to write Cucumber tests for my E-commerce application.
I want to write a test for shopping cart check out behavior. When I add item to the shopping cart and finally I do check out from the application then I should able to see my shopping cart items are correctly populated.
Scenario: Verify shopping cart check out behavior
When I add some items into my cart
And I check out from the application
Then I should able to see following items in my cart
| item | price | quantity |
| shoes | 200 | 2 |
| watch | 500 | 1 |
| mobile | 1000 | 1 |
Now for above Cucumber scenario, I have corresponding Cucumber Step definitions. We are interested in the last step which takes Data table as inputs.
I am following Page object pattern for writing my tests. If you are not familiar with it, you can check it out here
Page Object Pattern:
Within your web app's UI there are areas that your tests interact with. A Page Object simply models these as objects within the test code. This reduces the amount of duplicated code and means that if the UI changes, the fix need only be applied in one place.
Advantages of Page Object Pattern:
The Page Object Design Pattern provides the following advantages
1. There is a clean separation between test code and page specific code such as locators (or their use if you’re using a UI Map) and layout.
2. There is a single repository for the services or operations offered by the page rather than having these services scattered throughout the tests.
Step Definition Implementation without Async:
Following is the Step Definition code.
We have two input parameters as below –
1. dataTable : We will get the data that passed below step definition. For reference see our cucumber scenario section defined above.
2. Callback: Once our test execution completes we need to notify back to CucumberJS that we are done with the test. So that based on result CucumberJS will mark test Pass or Fail.
If you have given some default time out for functions and your method is taking time more than default timeout then test will fail due function timeout issue.
Now, I want to iterate on dataTable. DataTable.hashes() gives me list of rows. For each row, I will check the given item exists on the UI page.
For assertion purpose, I am using Chai and Chai as Promised library. You need to install this library on your environment.
Instead of manually wiring up your expectations to a promise's fulfilled and rejected handlers, we can directly use chai-as-promised for expecting result for a method which returns promise in the response.
Have you noticed any problem with the test?
Any idea, what is the problem?
It will always pass in any situation. Ohh God, my manager will kill me for writing a test like this.
First question comes in mind, why it is always passing?
There is one gotcha with our line number 7, we are invoking callback(). This will pass the control to the CucumberJs.
Is there any issue with line number 7? No, the problem lies in our implementation, we are iterating on items and calling an asynchronous method.
When line number 7 gets executed that time our line number 5 code still executing due to asynchronous implementation.
We should have called callback() once we are done with the all operations on items. How we will know that we have completed operations for all items?
That’s where Async library helps us smartly to do asynchronous implementation effectiverly.
Async libarary comes to rescue us:
Here is our final solution using Async Each libarary.
Let us understand, how Async.Each solves our problem.
We have passed three parameters to Async.each method –
1. We are passing collection of items that we want to iterate
2. Async function which will execute for each object on the collection. We are passing two parameters to this function, current object of the collection and callback
3. Final Callback which will get invoked either our all objects operation completes or if we get any error while executing some operation. This callback is taking “err” as an input parameter which is an Error object.
In case of any error, “err” will have value and if there is no error “err” will be null.
So, if you have notice unlike earlier implementation, our callback will be passed to CucumberJS once our all operations completes or in any case we get any error.
Hence, this test will return correct result.
I have shown that for asserting Cucumber data table where page object returns promise, it is needed to write smart test which will work for asynchronous implementation.
Using Async.each API, we can have flexibility and we can get callback exactly when all asynchronous operations gets completed. So that test results will correctly captured by the CucumberJS.
Async library is very powerful library, there are some different versions of each. Also, there are some other variations like async.map, async.every. If you are interested then you can look at the Async documentation: https://caolan.github.io/async/docs.html#each