w3resource

Testing Your App


In this tutorial you will learn the relationship between Cypress and your back end, we will show you how to configure Cypress to fit your application. We will show you how to create tests that works with (or without) your authentication mechanism. Finally, you will learn how to leverage test data.

Prerequisites

  1. You should have installed the Test Runner
  2. You should have opened the Test Runner in your project.

Step 1: Start Server

If you have met this conditions, you will need to start the local development server that hosts your application. It should be similar to http://localhost:3000

Why start a local development server?

You may be wondering- what is wrong with using an application that is already in production?

Although you can test an application that is already deployed, working with an already deployed application is not the sweet spot of Cypress.

Cypress is built, and optimized around being a tool for your daily local development. In fact, once you start using Cypress for a while, it is believed that you will find Cypress useful to do all your development in it.

The gains of using Cypress is not just that you will be able to test and develop at the same time, Cypress also enables you to be able to build your application faster while getting tests “for free”.

Also, because Cypress enables you to do things like stub network requests, you can build out your web application without even needing a server to provide valid JSON responses.

An attempt to shoehorn tests to an already built application is much more difficult than building it as you write tests. You will most likely encounter a series of initial up front challenges/hurdles that would have otherwise been avoided by writing tests from the start.

Finally, testing with the local server will give you the ability to control your application. When your application is running in production mode, you will not be able to control it.

Step 2: Visit your server

Once you get your server running, you can then visit it. To get started with testing your server, you have to create a file called homepage_test.js in your project’s cypress/integration folder.

Once you create this file, it will be shown in the list of your spec files. Now you can update the file to contain the following code:

'''describe('The Home Page', () => {
  it('successfully loads', () => {
    cy.visit('http://localhost:3000') // change URL to match your dev URL
  })
})'''

If you have previously started your server, then your application will load and work successfully.

Step 3: Configure Cypress

Rather than manually typing in the URL of your application for every test. You can use the Cypress configuration option to create a URL once for all.

When you open up the configuration file (cypress.json which is your project directory, by default).

it usually starts empty:

{}

Now we can add the baseUrl option.

{
  "baseUrl": "http://localhost:3000"
}

Once you do this, it will automatically prefix cy.visit() and cy.request() command with the baseUrl.

Anytime you modify the configuration file, Cypress reboots itself and kills any open browser.

Let us update the test file to reflect the relative path, omitting the hostname and port.

'''describe('The Home Page', () => {
  it('successfully loads', () => {
    cy.visit('http:/')
  })
})'''

Testing strategies

Every application has their own unique features. To test your application, the edge cases, regressions, seams and what to test depends on you, your application and your team.

However, here are some quick tips on common situations that you are likely going to run into.

Seeding data

Based on the way you built your application, it is likely that your web application is going to be affected and controlled by the server.

Modern servers communicate with frontend apps using JSON, however you could still be running a traditional server-side rendered HTML web application.

Traditionally, when you are writing e2e tests using Selenium, you have to do some set up and tear down on the server before you can automate the browser.

Perhaps you will need to generate a user, and seed them with associations and records. You are probably familiar with things like fixtures and factories.

There are generally three ways to facilitate this with Cypress:

  • cy.exec() –if you want to run system commands
  • cy.task() – if you need to run code in Node via the pluginsFile
  • cy.request() – if you want to make HTTP requests

If you are running node.js on your server, you can add a before or beforeEach hook that executes an npm task.

'''describe('The Home Page', () => {
  beforeEach(() => {
    // this will reset and seed the database before every test
    cy.exec('npm run db:reset && npm run db:seed')
  })

  it('successfully loads', () => {
    cy.visit('/')
  })
})'''

Stubbing the server

Rather than seeding, you can totally bypass the server. You will still receive all the HTML/JS/CSS assets from your server and you will continue to cy.visit() it in the same way, however, you can stub the JSON responses coming from it. This implies that you force the server to respond with what you want, rather than resetting the database and seeding it with the state that you want.

Additionally, stubbing the server will enable you to build out your application without the need for the contract of the server to exist. You will be able to build your application the way you want your data to be structures, it also enables you to test edge cases, without the need pf a server.

Although stubbing is good, it does not give you the guarantee that the response payloads actually matches what the server will send. There are valid ways to get around this like generating the fixture stubs ahead of time and writing a single e2e test without stubs and stubbing the rest.

Logging in

Testing the login to your application is arguably the hardest hurdle you will face while testing your application. Logging in slows down a test suite the most. But most of the good parts of your application will likely require an authenticated user.

Fully test the login flow –but do that once!

Adding your signup and login flow under test coverage since it is very important to all of your users and it is something you will not want to break.

Usually logging is a critical feature that will usually involve your server. It is recommended that you test signup and login using your UI, in much the same way a real user would.

An example is as shown below:

'''describe('The Login Page', () => {
  beforeEach(() => {
    // This will reset and seed the database before every test
    cy.exec('npm run db:reset && npm run db:seed')

    // seeds a user in the DB that we can control from our tests
    // assuming that it generates a random password for us
    cy.request('POST', '/test/seed/user', { username: 'jane.lane' })
      .its('body')
      .as('currentUser')
  })

  it('will set auth cookie when logging in via form submission', function () {
    // destructure the assignment of the this.currentUser object
    const { username, password } = this.currentUser

    cy.visit('/login')

    cy.get('input[name=username]').type(username)

    // {enter} will cause the form to submit
    cy.get('input[name=password]').type(`${password}{enter}`)

    // you should be redirected to /dashboard
    cy.url().should('include', '/dashboard')

    // your auth cookie should be present
    cy.getCookie('your-session-cookie').should('exist')

    // the UI should reflect this user being logged in
    cy.get('h1').should('contain', 'jane.lane')
  })
})'''

Bypassing your UI

If you are writing tests for a very specific feature, you will need to use the UI to test it.

But whenever you are testing another of the system that relies on a state from a previous feature, it is not advisable to use the UI to set up this state.

Logging into your application is a prerequisite of state that should come before all of your other tests.

Since Cypress is not Selenium, we can skip the need to use the UI with the use of cy.request().

cy.request() will automatically get and set cookies under the hood, and then you can use it to build up the state without using the UI of your browser and still have it perform exactly as if it came from the browser.

Here is a rewrite of the previous code to reflect this change:

'''describe('The Dashboard Page', () => {
  beforeEach(() => {
    // This resets and seeds the database prior to every test
    cy.exec('npm run db:reset && npm run db:seed')

    // seeds a user in the DB that we can control from our tests
    // assuming that it generates a random password for us
    cy.request('POST', '/test/seed/user', { username: 'jane.lane' })
      .its('body')
      .as('currentUser')
  })

  it('will log in programmatically without using the UI', function () {
    // destructure assignment of the this.currentUser object
    const { username, password } = this.currentUser

    // This will programmatically log us in without needing the UI
    cy.request('POST', '/login', {
      username,
      password
    })

    // now that you are logged in, you can visit
    // any of restricted route!
    cy.visit('/dashboard')

    // your auth cookie should be present
    cy.getCookie('your-session-cookie').should('exist')

    //The UI should reflect this user being logged in
    cy.get('h1').should('contain', 'jane.lane')
  })
})'''