Tuesday, 15 December 2015

Protractor test suites as Promises



In this article I will demonstrate you another way how to write effective and re-usables tests using Protractor. This time I will show you how to use Promises as more effective way of dealing with results of evalutions of test suites and how to combine test suites togther.


As of scenario for our test let's again assume that our imaginary web application that we want to test has a login functionality. In other words each test that we want to implement should pass authorization first.
Let's implement this.
For this purpose I will the same "login suite" 
describe('login suite', function() {
    var protractor;
    var loginButton = element(by.css('#submitButton')).click();

    beforeEach(function(){
        protractor = protractor.getInstance();
    });

    afterEach(function(){
        //e.g. we need to have "clean" state after execution of each test - no auth cookies 
        protractor.manage().deleteAllCookies();
    });

    it('can get content of protected page1', function() {
        protractor.get('/login-protected-page-1/');

        element(by.model('model.login')).sendKeys('yourlogin');
        element(by.input('model.password')).sendKeys('yourpassword');

        loginButton.click().then(function() {
            //the ONLY important part of your test
            var elems = element.all(by.repeater('d in data'));
            expect(elems.count()).toBe(30);
        }); 
    });

    it('can get content of protected page2', function() {
        protractor.get('/login-protected-page-2/');

        element(by.model('model.login')).sendKeys('yourlogin');
        element(by.input('model.password')).sendKeys('yourpassword');

        loginButton.click().then(function() {
            //the ONLY important part of your test
            var elems = element.all(by.repeater('d in data'));
            expect(elems.count()).toBe(15);
        }); 
    });
});
Ok. Nothing special here. Now let's have a look how we could improve this code in order to use login in other test suites without loosing in readability and keeping our code clean and DRY.
This time we will apply power of Promises.
Let's re-write first "Login part" (login_test.js):
var loginSuite = function(suiteName, specName, url) {
    var defer = protractor.promise.defer();

    describe(suiteName, function() {
        var prot = protractor.getInstance();

        afterEach(function() {
            protractor.manage().deleteAllCookies();
        });

        it(specName, function() {
            prot.get(url);
            element(by.model('model.username')).sendKeys('yourlogin');
            element(by.model('model.password')).sendKeys('yourpassword');
            element(by.id('loginButton')).click().then(defer.fulfill, defer.reject);
        });
    });

    return defer.promise;
};
Main difference here from original code is that we return Promise object which will be fullfilled on login form submission event.
And now time to re-write "assertion part" of our original test suite:
var loginSuite = require('./login_test.js');

loginSuite('suite for page1', 'can get content of protected page1', '/login-protected-page-1/')
    .then(
        function() {
            var elems = element.all(by.repeater('d in data'));
            expect(elems.count()).toBe(30);
        }
    );

loginSuite('suite for page2', 'can get content of protected page2', '/login-protected-page-2/')
    .then(
        function() {
            var elems = element.all(by.repeater('d in data'));
            expect(elems.count()).toBe(15);
        }
    );
That's was it.
In conclusion I would say that using Promises in test suites in favor of callbacks in Protractor looks more natural (in fact whole Protractor API utilize promises). Especially in situations when you will start to implement more complex scenarious where one suites will rely on multiple other suites. This would definitely make debugging and implementation much more easier.
Some usefull links:

No comments:

Post a Comment