Protractor Before and Afters

If you're not familiar with Protractor, it's a Node.js program that runs end-to-end tests written in JavaScript. Protractor uses WebDriver to control browsers and simulate user actions.

Protractor uses Jasmine for its test syntax. As in unit testing, a test file is comprised of one or more it blocks that describe the requirements of your application. it blocks are made of commands and expectations. Commands tell Protractor to do something with the application such as navigate to a page or click on a button. Expectations tell Protractor to assert something about the application's state, such as the value of a field or the current URL.

Check out the Angular Docs for more on testing your Angular app with Protractor. See the Protractor docs for more information on Protractor as well.

This post is going to explore of some of the tools that Jasmine and Protractor provide that allow us to execute code at specific times; from before the test environment is even setup, all the way through and after the test enviroment is shut down. We're mostly going to be looking at the execution order of the different options and how they relate to each other.

Jasmine Tools

To help a test suite DRY up any duplicated setup and teardown code, Jasmine provides the global beforeEach, afterEach, beforeAll, and afterAll functions.

As the name implies, the beforeEach function is called once before each spec (it block) in the describe in which it is called, and the afterEach function is called once after each spec.

Here is the same set of specs written a little differently. The variable under test is defined at the top-level scope — the describe block — and initialization code is moved into a beforeEach function. The afterEach function resets the variable before continuing.

The beforeAll function is only called once and is called before all the specs in describe are run, and the afterAll function is called after all specs finish. These functions can be used to speed up test suites with expensive setup and teardown.

However, be careful using beforeAll and afterAll! Since they are not reset between specs, it is easy to accidentally leak state between your specs so that they erroneously pass or fail.

You can read more about this in the Jasmine docs at jasmine.github.io.

Protractor Tools

These Protractor tools can be defined in the protractor configuration file

beforeLaunch - A callback function called once configs are read but before any environment setup. This will only run once, and is run before onPrepare. You can specify a file containing code to run by setting beforeLaunch to the filename string.

onPrepare - A callback function called once protractor is ready and available and before the specs are executed. If multiple capabilities are being run, this will run once per capability. You can specify a file containing code to run by setting onPrepare to the filename string. If onPrepare returns a promise (optional), Protractor will wait for the promise to resolve before continuing. This can be used if the preparation involves any asynchronous calls, e.g. interacting with the browser. Otherwise Protractor cannot guarantee order of execution and may start the tests before preparation finishes.

onComplete - A callback function called once all tests are finished. If onComplete returns a promise (optional), Protractor will wait for the promise to resolve before shutting down webdriver. At the time onComplete runs, tests will be done but global objects will still be available.

onCleanUp - A callback function called once the tests have finished running and the WebDriver instance has been shut down. It is passed the exit code (0 if the tests passed). This is called once per capability.

afterLaunch - A callback function called once all tests have finished running and the WebDriver instance has been shut down. It is passed the exit code (0 if the tests passed). If you want asynchronous code to be executed before the program exits, afterLaunch must return a promise. This is called only once before the program exits (after onCleanUp).

More on this in the Protractor Docs.

Example: Order of Operations

So what is the order of execution for each of these tools? Lets assume our protractor configuration and test file looks something like the following:

// protractor.config.js
module.exports = {  
    // ...

    beforeLaunch: () => {
        console.log('beforeLaunch');
    },

    onPrepare: () => {
        console.log('onPrepare');
    },

    onComplete: () => {
        console.log('onComplete');
    },

    onCleanUp: () => {
        console.log('onCleanUp');
    },

    afterLaunch: () => {
        console.log('afterLaunch');
    }
};
// someTest.spec.js
describe('describe block', () => {  
    beforeAll(() => {
        console.log('beforeAll');
    });

    beforeEach(() => {
        console.log('beforeEach');
    });

    afterEach(() => {
        console.log('afterEach');
    });

    afterAll(() => {
        console.log('afterAll');
    });

    it('test 1', () => {
        console.log('Test 1');
    });

    it('test 2', () => {
        console.log('Test 2');
    });
});

When the tests are run, the output would be:

beforeLaunch  
Starting selenium standalone server...  
[launcher] Running 1 instances of WebDriver
Selenium standalone server started at http://192.168.41.116:52569/wd/hub  
onPrepare  
Started  
beforeAll  
beforeEach  
Test 1  
afterEach  
beforeEach  
Test 2  
afterEach  
afterAll

2 specs, 0 failures  
Finished in 0.013 seconds  
onComplete  
Shutting down selenium standalone server.  
onCleanUp  
[launcher] 0 instance(s) of WebDriver still running
[launcher] firefox #1 passed
afterLaunch  

Cleaning up the output a little, we can see that the order of operations here can be summarized as such:

  1. beforeLaunch
  2. onPrepare
  3. beforeAll
  4. For each test, the following is executed:
    1. beforeEach
    2. the actual test is run
    3. afterEach
  5. afterAll
  6. onComplete
  7. onCleanup
  8. afterLaunch

Where all of the Jasmine tools are run right after the onPrepare and are all complete before the next protractor config function, onComplete, is called.

Example: Nested before*s

The order of execution of beforeEach and beforeAll in nested describe blocks is a common source of confusion as well. The beforeAll and afterAll functions wrap all the specs where the beforeEach and afterEach functions wrap each spec individually. So for the following test:

describe('describe block 1', () => {  
    beforeEach(function () {
        console.log('beforeEach 1');
    });

    describe('describe block 2', () => {
        beforeAll(() => {
            console.log('beforeAll 2');
        });

        it('test 1', () => {
            console.log('Test 1');
        });

        it('test 2', () => {
            console.log('Test 2');
        });
    });
});

The output would be:

beforeAll 2  
beforeEach 1  
Test 1  
beforeEach 1  
Test 2  

Because the beforeAll function is only called once it will always be called before any beforeEach function that would be applied to the test- even if the beforeEach is in a parent describe block as seen in this example.

Example: Nest All The Things!

Okay, so we have a good understanding of how beforeEach and beforeAll works with nested describe blocks. What happens if both describe blocks have a beforeEach and beforeAll? What if they both also have aferEach and afterAll?

To find out, we just set that up in the test file, for this example, lets assume it looks something like this:

describe('describe block 1', () => {  
    beforeAll(() => {
        console.log('beforeAll 1');
    });

    beforeEach(() => {
        console.log('beforeEach 1');
    });

    afterEach(() => {
        console.log('afterEach 1');
    });

    afterAll(() => {
        console.log('afterAll 1');
    });

    it('test 1.1', () => {
        console.log('Test 1.1');
    });

    describe('describe block 2', () => {
        beforeAll(() => {
            console.log('beforeAll 2');
        });

        beforeEach(() => {
            console.log('beforeEach 2');
        });

        afterEach(() => {
            console.log('afterEach 2');
        });

        afterAll(() => {
            console.log('afterAll 2');
        });

        it('test 2.1', () => {
            console.log('Test 2.1');
        });

        it('test 2.2', () => {
            console.log('Test 2.2');
        });
    });
});

Given that, the output should look like the following:

beforeAll 1  
beforeEach 1  
Test 1.1  
afterEach 1  
beforeAll 2  
beforeEach 1  
beforeEach 2  
Test 2.1  
afterEach 2  
afterEach 1  
beforeEach 1  
beforeEach 2  
Test 2.2  
afterEach 2  
afterEach 1  
afterAll 2  
afterAll 1  

So we can see this works the same way as the previous example, just with a little more stuff going on. The beforeAll and afterAll functions wrap all the tests within their respective describe block, and the beforeEach and afterEach functions wrap each test individually. Visually simplified, this hierarchy would look something like:

  1. beforeAll (level 1)
  2. For each test in the root level:
    1. beforeEach (level 1)
    2. Test runs.
    3. afterEach (level 1)
  3. For each child describe block within level 1:
    1. beforeAll (level 2)
    2. For each test in the root level:
      1. beforeEach (level 1)
      2. beforeEach (level 2)
      3. Test runs.
      4. afterEach (level 2)
      5. afterEach (level 1)
    3. For each child describe block within level 2:
      1. etc.
    4. afterAll (level 2)
  4. afterAll (level 1)