Simplify your Protractor tests using Generator functions
Writing protractor tests can be quite difficult, because of all the asynchronous calls you have to deal with. Even promise-chaining does not help when your tests become longer and more complex. With Javascript generator functions it is possible to simplify your tests.
Example
Let’s say we have a todo-app created in AngularJS with two pages: a page with a list of todos and a page where a user can create a todo-item. We have two page objects created that can communicate with the pages in our Protractor tests. This is an example of an actual testcase with the use of promise chaining:
todo.spec.js - with promises.
describe("todo test", function () {
const todoListPage = require("../page-objects/todo-list.page.po");
const todoCreatePage = require("../page-objects/todo-create.page.po");
beforeEach(() => {
browser.get("/");
});
it("should create a todo item", () => {
//Check that number of items is 0 to begin with.
todoListPage
.open()
.then(() => todoListPage.getNumberOfItems())
.then((nrOfItems) => {
expect(nrOfItems).toBe(0);
})
.then(() => todoCreatePage.open())
.then(() => todoCreatePage.createTodoItem("test"))
.then(() => todoListPage.open())
.then(() => todoListPage.getNumberOfItems())
.then((nrOfItems) => {
//number of todos should now be 1
expect(nrOfItems).toBe(1);
});
});
});
todo.spec.js - with generators.
describe("todo test", function () {
const todoListPage = require("../page-objects/todo-list.page.po");
const todoCreatePage = require("../page-objects/todo-create.page.po");
beforeEach(() => {
browser.get("/");
});
it("should create a todo item", function* () {
//Check that number of items is 0 to begin with.
yield todoListPage.open();
expect(yield todoListPage.getNumberOfItems()).toBe(0);
//Add
yield todoCreatePage.open();
yield todoCreatePage.createTodoItem("test");
yield todoListPage.open();
//number of todos should now be 1
expect(yield todoListPage.getNumberOfItems()).toBe(1);
});
});
As you can see, the testcode is much easier to understand when the generator method is used.
How does it work?
The generator method which is defined with the function*(){}
syntax can be
used together with the
jasmine-co library. jasmine-co wraps
all methods and waits for each yield statement to be resolved before continuing
to the next yield statement, even if this yield statement is resolved
asynchronously.
Setup
Install jasmine-co dependency.
npm install jasmine-co --save-dev --save-exact
Install jasmine-co in the protractor.conf.js file.
Example protractor.conf.js.
```javascript exports.config = { seleniumAddress: "http://localhost:4444/wd/hub",
getPageTimeout: 60000, allScriptsTimeout: 500000, baseUrl: "http://localhost:3000",
capabilities: { browserName: "chrome", chromeOptions: { args: ["--disable-extensions", "--start-maximized"], }, },
specs: ["test/component-tests//.spec.js"], framework: "jasmine", jasmineNodeOpts: { showColors: true, defaultTimeoutInterval: 3000000, },
onPrepare: function () { //This will wrap every test with jasmineCo(); Just like in the docs: https://www.npmjs.com/package/jasmine-co require("jasmine-co").install();
//Define a shortcut for protractor.ExpectedConditions
global.EC = protractor.ExpectedConditions;
}, }; ```
Start rewriting your tests incremently.
Important: It is not necessary to rewrite all your tests to generators, so you can try it out on one test first.
Source code
Look at the complete source code https://github.com/baconcutter/protractor-generator-example.