How To Run Browser Tests via Cypress in Your CI/CD Pipeline with AWS CodeBuild
Tags: aws, webdev, devops, testing
Published: 2018-08-14 16:24:23

---
title: How To Run Browser Tests via Cypress in Your CI/CD Pipeline with AWS CodeBuild
published: true
description: Run browser tests in your CI/CD Pipeline without Selenium.
tags: #aws #webdev #devops #testing
cover_image: https://thepracticaldev.s3.amazonaws.com/i/k0xyemjp77beajima9ut.png
canonical_url: https://blog.kylegalbraith.com/2018/08/14/how-to-run-browser-tests-via-cypress-in-your-cicd-pipeline-with-aws-codebuild/
---
Recently I launched my own [blog](https://blog.kylegalbraith.com). During that process, I got interested in how I can automate testing the quality of my new blog. I didn't want a heavy duty framework. I didn't want deep tests because the functionality on my blog isn't very verbose.

A basic thing I wanted to test was the mobile view of my blog versus the tablet/desktop view. I often make the mistake of changing a CSS class or HTML element and breaking the mobile layout in some way I hadn't thought about.

So, I started looking at how to simplify my browser testing. There were a few things I wanted to consider in choosing a framework.

* I wanted it to be lightweight, ideally a single `npm install` would be ideal.
* It needed to be easy to integrate into my [AWS CodePipelie CI/CD pipeline](https://blog.kylegalbraith.com/2018/05/14/creating-a-cicd-pipeline-with-a-git-repository-in-~30-seconds-using-terraform-and-aws/).
* The test setup, assertions, and framework components should be sensible.

A quick evaluation of these led me to **not** go with the classic Selenium because I found it to be bulky the last time I used it. However, I like the idea of testing components via the browser, but I prefer it to be lightweight.

### Enter stage right: [Cypress.io](https://www.cypress.io/?ref=kylegalbraith)
I first came across Cypress via a Tweet from Kent Dodds last year.

{% twitter 835897600263892992 %}

If you read the thread you will see that it was in response to users running into browser issues that unit tests just weren't catching. The user, in this case, used Jest to write their tests but were finding browser issues when they went to production.

Unit tests are fantastic, and they are a must-have for front-end and back-end services. But if we recall the testing pyramid from Martin Fowler.

![Testing pyramid from Martin Fowler](https://thepracticaldev.s3.amazonaws.com/i/udhv9d2k7o8nsrfkgopv.png)

We see that unit tests are the biggest blob. They should be fast and inexpensive.

But, the other blobs, Service, and UI are important as well.

What the tweet above highlights is that unit tests are a large piece of the pie, but not the whole pie.

In my mind, this is where Cypress comes into play.

I can write my unit tests using whatever framework I'm most comfortable with whether that be NUnit, Mocha, Pyunit, etc. But for front-end services like my React based blog, I need to add UI tests or end to end tests that test pieces of functionality or how things are displayed.

### Writing the Cypress tests
Getting started with Cypress is very simple. We can just install it as a dev dependency in our project.

```bash
$ npm install cypress --save-dev
```

Once installed we can create a few new directories and our first test fixture.

```bash
$ mkdir cypress/integration/blog-verify
$ touch landing.spec.js
```

For my blog landing page, I am going to write the following cypress test inside of `landing.spec.js`.

```javascript
/// <reference types="Cypress" />

context('Landing', () => {
    beforeEach(() => {
        cy.visit('https://blog.kylegalbraith.com')
    })

    it('center header on small screens', () => {
        cy.get('.container > div')
            .should('have.css', 'text-align')
            .and('match', /left/);

        cy.viewport('iphone-6+');

        cy.get('.container > div')
            .should('have.css', 'text-align')
            .and('match', /center/);
    });
});
```
Here I have a simple cypress test that is validating the position of some header text on my blog landing page. Notice that in the `beforeEach` we are navigating to my blog before each test in the fixture.

Then in the test `center header on small screen` I am using the cypress library with Chai like assertions to verify my results. First, I check that the CSS of my header is set to `text-align: left;` with a normal size browser. Then I set the browser width to a mobile view, `cy.viewport('iphone-6+');` in order to verify the text changes to `center`.

This is a basic test in cypress. It is only scratching the surface of what can be done, so if you are looking to check more complex scenarios check out the [documentation on how to interact with various elements](https://docs.cypress.io/guides/core-concepts/interacting-with-elements.html#Additionally-an-element-is-considered-hidden-if).

Now to run the test we can execute Cypress from our `node_modules` folder.

```bash
$ node_modules/.bin/cypress run
Running: blog-verify/landing.spec.js...                                                

  Landing
    √ center header on small screens (4524ms)


  1 passing (7s)

  ┌───────────────────────────────────────────┐
  │ Tests:        1                           │
  │ Passing:      1                           │
  │ Failing:      0                           │
  │ Pending:      0                           │
  │ Skipped:      0                           │
  │ Screenshots:  0                           │
  │ Video:        true                        │
  │ Duration:     7 seconds                   │
  │ Spec Ran:     blog-verify/landing.spec.js │
  └───────────────────────────────────────────┘
```

Once a test run completes you can also view the video recordings for each test in the `videos` directory located in your `cypress` directory.

![Cypress output video](https://thepracticaldev.s3.amazonaws.com/i/d9w2oj858wp9vnb7yytn.gif)

Pretty cool right? No Selenium, no Chrome browser, and no bulky setup to get going. One package install, write a test and run.

Of course, this is a basic example of what can be done with Cypress. I encourage you to explore adding integration tests that cover the use cases of your user. Examples of these could be:

* Logging into and out of your application.
* Saving and loading application data.
* Checking data is viewable in different browser sizes.
* Validating users with certain permissions can't access other parts of your application.

To wrap things up let's explore how we can incorporate these tests into your continuous integration and deployment pipeline living in AWS.

### Incorporating Cypress into AWS CodePipeline and CodeBuild
If you recall from a previous blog post, [I developed a Terraform module that provisions a Git repo, AWS CodePipeline, and AWS CodeBuild projects](https://blog.kylegalbraith.com/2018/05/14/creating-a-cicd-pipeline-with-a-git-repository-in-~30-seconds-using-terraform-and-aws/) so that I can have an entire end to end CI/CD pipeline for all my new projects. For my blog, I used this module so that I can continuously deploy my blog with any new commits to `master`.

To run these new tests I simply need to update the `buildspec.yml` file in my repository. This YAML file is what tells AWS CodeBuild how to build, test, and deploy my static website.

Here is what my `buildspec.yml` file looks like before I update it to run my tests.

```yaml
version: 0.2
phases:
  install:
    commands:
      - echo "install cypress dependencies..."
      - npm install -g gatsby-cli
      - npm install -g purgecss
  pre_build:
    commands:
      - echo "pre_build step"
  build:
    commands:
      - cd src
      - cd new-gatsby-blog
      - npm install
      - npm run-script build
      - aws s3 cp public/ "s3://blog.kylegalbraith.com/" --recursive --cache-control 'public, max-age=2628000'
      - aws cloudfront create-invalidation --distribution-id E3SMHPICHT13MG --paths '/*'
  post_build:
    commands:
      - echo "post_build step"
```

In order to run our tests, I am going to update the `install` section to add a few dependencies that Cypress relies on. I am also going to update `build` section to run our tests after we have deployed the site to S3 and invalidated the CloudFront cache. Therefore, think of these tests as *post-deployment* verifications.

Here is the `buildspec.yml` file after making these changes.

```yaml
version: 0.2
phases:
  install:
    commands:
      - echo "install cypress dependencies..."
      - npm install -g gatsby-cli
      - npm install -g purgecss
      - apt-get update
      - apt-get install -y libgtk2.0-0 libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 xvfb
  pre_build:
    commands:
      - echo "pre_build step"
  build:
    commands:
      - cd src
      - cd new-gatsby-blog
      - npm install
      - npm run-script build
      - aws s3 cp public/ "s3://blog.kylegalbraith.com/" --recursive --cache-control 'public, max-age=2628000'
      - aws cloudfront create-invalidation --distribution-id E3SMHPICHT13MG --paths '/*'
      - node_modules/.bin/cypress run
  post_build:
    commands:
      - echo "post_build step"
```

Notice here inside of the `install` step we are running:

```bash
apt-get install -y libgtk2.0-0 libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 xvfb
```

This is to install the necessary dependencies that Cypress needs in order to run inside of a CodeBuild project.

Then inside of the `build` step, we execute the tests:

```bash
node_modules/.bin/cypress run
```

Now when I check in the changes my AWS CodePipeline will be triggered.

Once the build completes, I can check my AWS CodeBuild logs and see the tests executed and the build was successful.

```bash
  Running: blog-verify/landing.spec.js...                              (2 of 2) 


  Landing

    ✓ center header on small screens (697ms)


  1 passing (2s)


  (Results)

  ┌───────────────────────────────────────────┐
  │ Tests:        1                           │
  │ Passing:      1                           │
  │ Failing:      0                           │
  │ Pending:      0                           │
  │ Skipped:      0                           │
  │ Screenshots:  0                           │
  │ Video:        true                        │
  │ Duration:     2 seconds                   │
  │ Spec Ran:     blog-verify/landing.spec.js │
  └───────────────────────────────────────────┘


  (Video)

  - Started processing:   Compressing to 32 CRF
  - Finished processing:  /codebuild/output/src459391942/src/src/new-gatsby-blog/cypress/videos/blog-verify/landing.spec.js.mp4 (1 second)


================================================================================

  (Run Finished)


      Spec                                    Tests  Pass…  Fail…  Pend…  Skip… 
  ┌────────────────────────────────────────────────────────────────────────────┐
  │ ✔ blog-verify/individual_post.…   00:02      1      1      -      -      - │
  ├────────────────────────────────────────────────────────────────────────────┤
  │ ✔ blog-verify/landing.spec.js     00:02      1      1      -      -      - │
  └────────────────────────────────────────────────────────────────────────────┘
    All specs passed!                 00:05      2      2      -      -      -  

[?25h
[Container] 2018/08/12 00:59:08 Phase complete: BUILD Success: true
[Container] 2018/08/12 00:59:08 Phase context status code:  Message: 
[Container] 2018/08/12 00:59:08 Entering phase POST_BUILD
[Container] 2018/08/12 00:59:08 Running command echo "post_build step - run cypress"
post_build step - run cypress

[Container] 2018/08/12 00:59:08 Phase complete: POST_BUILD Success: true
```

You can see that we get a detailed breakdown of our test execution. We can even copy the video files out of our CodeBuild run if we wanted to. Very cool!

### Conclusion
In this post, we talked about why end to end or integration tests are important. No one is saying you should go all in on integration/UI tests, but you should at least cover the main pieces of functionality for your application with them. They are **not** a replacement for great unit tests, but they **are** an excellent complement.

We only scratched the surface of what Cypress has to offer. I intend to explore it further to test more of my [complex applications](https:///www.ultimatefantasysupercross.com). Hopefully, this has given you an introduction to UI testing and demonstrated how it can be easily incorporated into your CI/CD Pipeline. 

As always, if you have any questions please feel free to leave me a comment below or reach out to me [Twitter](https://twitter.com/kylegalbraith).

### Are you hungry to learn more about Amazon Web Services?

Want to learn more about AWS? I recently released an e-book and video course that cuts through the sea of information. It focuses on hosting, securing, and deploying static websites on AWS. The goal is to learn services related to this problem as you are using them. [If you have been wanting to learn AWS, but you’re not sure where to start, then check out my course](https://www.kylegalbraith.com/learn-aws).