Cypress End-To-End Tests with Angular

Hack your e2e tests like a pro with angular debugging tools and inject test data

Posted by Gregor Tätzner


This is part 2 of our angular debugging series and incorporates Cypress - an excellent e2e testing framework. If you missed part 1, you can find it here. It helps you in understanding this post. You can find all code examples of this post also in this project.

Install Cypress

Lets start by configuring cypress for your angular project. Don’t be afraid, it’s quite easy :) If you are already running cypress, you may skip this part.

First install cypress:

# yarn users
yarn add -D cypress
# npm users
npm i cypress --save-dev

At this point you may just replace the e2e test command usually generated by angular-cli:

// replace/add in package.json in the scripts section
"e2e": "cypress open",

And when your at it remove protractor alltogether:

# yarn
yarn remove protractor
# npm
npm r protractor

You can also drop the protractor configuration in angular.json, usually something like this:

"yourproject-e2e": {
  "root": "e2e",
  "sourceRoot": "e2e",
  "projectType": "application",
  "architect": {
    "e2e": {
      "builder": "@angular-devkit/build-angular:protractor",
      "options": {
        "protractorConfig": "./protractor.conf.js",
        "devServerTarget": "yourproject:serve"
      }
    },
    "lint": {
      "builder": "@angular-devkit/build-angular:tslint",
      "options": {
        "tsConfig": ["e2e/tsconfig.e2e.json"],
        "exclude": ["**/node_modules/**"]
      }
    }
  }
},

Finally you can delete the old e2e folder and run cypress with npm run e2e. This should create a new cypress folder with example tests and open the interactive cypress shell. It’s also possible to write cypress tests in typescript, but for now lets stay with good old ES6.

Hack your tests

I don’t want to elaborate too much on the cypress framework and how to write basic tests. If this is your first time with cypress just head to the great Getting Started Guide. When you feel comfortable with the test suite just come back here, but basically it looks similar to the default protractor/karma tests in angular - though there are some noteworthy differences.

Now lets cut down to the cheese and add a silly test to our application:

// app.spec.js
describe("App", () => {
  beforeEach(() => {
    // go to main page
    cy.visit("/");
  });

  it("should display main page", () => {
    // check our page header
    cy.contains("Welcome to cypress-tests");

    cy.window().then(win => {
      // get the root component of our app
      const root = win.ng.probe(win.getAllAngularRootElements()[0])
      const component = root.componentInstance;

      // set the title of the component (see app.component.ts)
      component.title = "Angular";

      // trigger change detection, so we can actually see the new title
      root.injector.get(win.ng.coreTokens.ApplicationRef).tick();

      // and there it is
      cy.contains("Welcome to Angular");
    });
  });
});

The first beforeEach block just navigates to the app, we only have a single route:

beforeEach(() => {
  // go to main page
  cy.visit("/");
});

But the interesting part starts with cy.window:

cy.window().then(win => {
  // get the root component of our app
  const root = win.ng.probe(win.getAllAngularRootElements()[0])
  const component = root.componentInstance;

  // set the title of the component (see app.component.ts)
  component.title = "Angular";

  // trigger change detection, so we can actually see the new title
  root.injector.get(win.ng.coreTokens.ApplicationRef).tick();

  // and there it is
  cy.contains("Welcome to Angular");
});

It allows you to access the window object of your running application and with it the dom and the angular debugging interface. In this example we get our root component and modify the title from Welcome to cypress-tests to Welcome to Angular and assert the change with cypress. Of course this is just a simple case, but imagine you have some data service/ redux store and inject test data to test specific behaviour of your application! In normal e2e tests this might be might be difficult to achieve: you would need to inject different fixtures into your db, somehow mock the api or api responses. Instead you can directly work with the apps model and force required states!

As in all cases with great power comes great responsibility, so don’t abuse this feature :) Don’t forget you can check out a fully working project with angular and cypress tests here.