Skip to content

End to End Testing (E2E)

Nick Clifford edited this page May 28, 2020 · 1 revision

What is E2E testing?

End-to-end testing is how we can test actual user behavior on the frontend. Using an automated browser driver, we can write code to simulate user actions like clicking buttons, filling out forms, and scrolling around a page. At the same time, we can hook into the browser's display behavior and test for various conditions like whether an element is disabled and the text inside elements.

Lucky for us, Angular comes with an E2E testing framework called Protractor (see what they did there?).

Why do we need this?

It's important to make sure every component of a project is functional when making changes to code. For example, you could have a perfectly secure, accurate, and performant backend, but if your frontend can't properly interact with it, then you'll still have problems. There's nothing wrong with testing these manually, but it can become quite a pain, especially when you're trying to check a specific edge case. Frameworks like Protractor make it easy to do automatically.

Running tests

From the root directory, run npm run e2e or ng e2e.

Troubleshooting

  • Make sure you have Chrome or Firefox installed, depending on what the current Protractor config (e2e/protractor.conf.js) says.
  • If Protractor yells at you for not having the right driver installed, try npx webdriver-manager update.
  • Try removing your node_modules folder and reinstalling with npm install -D.
  • Google is your friend! Protractor is notorious for sometimes having cryptic errors, so make sure to look them up and try whatever you can find.

Writing tests

When writing tests for a page, there's two components to be aware of: the page object and the spec file.

Page objects

Page objects go inside the e2e/page-objects folder and are named {page}.po.ts by convention. Each page object file contains a class dedicated to the page containing relevant methods and accessors to make specs cleaner and easier to write. All page objects should have a navigateTo() method that takes the browser to that page.

export class SomethingPage {
    async navigateTo() {
        await browser.get('/something');
    }

    get somethingButton() {
        return element(by.buttonText('Do Something'));
    }

    get somethingTextBox() {
        return element(by.css('.my-text-box'));
    }

    async somethingComplex(exampleParameter: string) {
        // do something complex that's commonly needed in specs
        // don't write a whole method just to click a button!
    }
}

For more examples, see existing page objects.

Spec files

Spec files contain the test suite for each page. These are in the e2e directory and must be named {page}.e2e-spec.ts for Protractor to find them.

Each suite consists of a describe block and at least one it block. A describe block creates a new scope, which is mainly used to designate features or different scenarios within a feature. it blocks define tests to run. There are also several different kinds of lifecycle hooks available, such as beforeEach, afterEach, beforeAll, and afterAll. Take a look at the Jasmine documentation for details. Note that these functions do not need to be explicitly imported.

Assertions are done with the expect function, which has several different matchers available on it. Some common ones are .toBe(), .toBeTruthy(), and .toBeNull(), to name a few. The Jasmine API docs has a list of all the available ones.

describe('Something', () => {
    let page: SomethingPage;

    beforeAll(() => {
        page = new SomethingPage();
    });

    beforeEach(async () => {
        await page.navigateTo();
    });

    it('does a thing', async () => {
        await page.somethingButton.click();
        expect(await page.somethingTextBox.getText()).toContain('blah blah blah');
    });

    describe('another scenario within Something', () => {
        it('does another thing', async () => {
            await page.somethingComplex();
            // try to avoid hardcoding wait times, take a look at the `browser.wait` method
            await browser.sleep(2500);
            expect(await page.somethingTextBox.getText()).toContain('something different');
        });
    });
});

For more examples, see existing spec files.

Clone this wiki locally