Protractor-perf: Performance regression testing

Reuse Protractor E2E test cases to check for performance regressions using protractor-perf


Functional testing guards the app from code changes in the future that may break the app. Performance is also a feature and hence it is equally important to ensure that code changes do not slow down a web page.
Angular lays a lot of emphasis on testing. Protractor runs end-to-end test scenarios and verifies the correctness of apps. These end-to-end scenarios can also be used to track performance of Angular app. Protractor-perf is a tool that reuses protractor's test cases to record performance metric and ensure that all key performance indicators stay within the performance budget.

Protractor-perf is based on browser-perf and records website runtime rendering metrics like frame rates, layout times, expensive event handlers, etc.

Using protractor-perf is pretty simple. Ensure that protractor is setup and can run the end to end test cases. Then, add a little code in the test cases to indicate when to start and end performance measurements, and run $ protractor-perf conf.js instead of $ protractor conf.js. Assertions can then to be used to ensure that the performance metrics that are recorded stay within the expected range. Here is a example protractor test case, with the instrumentation statements added.


var ProtractorPerf = require('protractor-perf');
describe('angularjs homepage todo list', function() {
    var perf = new ProtractorPerf(protractor); // Initialize the perf runner
    it('should add a todo', function() {
        browser.get('http://www.angularjs.org');

        perf.start(); // Start measuring the metrics
        element(by.model('todoText')).sendKeys('write a protractor test');
        element(by.css('[value="add"]')).click();
        perf.stop(); // Stop measuring the metrics 

        if (perf.isEnabled) { // Is perf measuring enabled ?
            // Check for perf regressions, just like you check for functional regressions
            expect(perf.getStats('meanFrameTime')).toBeLessThan(60); 
        };

        var todoList = element.all(by.repeater('todo in todos'));
        expect(todoList.count()).toEqual(3);
    });
});

First, protractor-perf is initialized using new ProtractorPerf(protractor). The global protractor reference is passed in. The statements perf.start() and perf.stop() indicate when to start and stop recording the performance metrics. Finally, perf.isEnabled is used to check if the test case was run using protractor-perf. If this test case is executed using regular protractor, the perf statements become no-ops. This way, you can still use the instrumented code for regular functional testing also. The perf.getStats is then used to ensure that the requirement performance metrics stay within the acceptable ranges.
A quick way to include performance tests for all scenarios would be to start the tests in a beforeEach function, and adding the asserts in a helper function that is called towards the end of each individual spec block.
If you have an angular app that is tested using protractor, give protractor-perf a spin. I would love to hear your experience and if the tool was a useful addition to your workflow. Ping me if you have any questions and I would be glad to help you out.