w3resource

Writing and Organizing Tests


In this tutorial, you will learn how to organize tests and support files. We will look at the languages that are supported in your test files, how Cypress handles integration tests and unit tests. And finally we will look at how to group your tests.

Folder Structure

Once you have added a new project to Cypress, Cypress will automatically scaffold out a suggested folder structure. It will create:

'''/cypress
  /fixtures
    - example.json

  /integration
    /examples
      - actions.spec.js
      - aliasing.spec.js
      - assertions.spec.js
      - connectors.spec.js
      - cookies.spec.js
      - cypress_api.spec.js
      - files.spec.js
      - local_storage.spec.js
      - location.spec.js
      - misc.spec.js
      - navigation.spec.js
      - network_requests.spec.js
      - querying.spec.js
      - spies_stubs_clocks.spec.js
      - traversal.spec.js
      - utilities.spec.js
      - viewport.spec.js
      - waiting.spec.js
      - window.spec.js

  /plugins
    - index.js

  /support
    - commands.js
    - index.js'''

As a default configuration.

Configuring Folder Structure

Although, Cypress permits you to configure where your tests, fixtures, and support files are located, it is recommended that you use the above folder structure when you are starting a project.

To modify the folder configuration, you have to do that in the configuration file, more on this in our Cypress configuration.

Fixture files

By default, the test files are located in cypress/integration. But you can configure it to another directory. The test files may be written as .js, .jsx, .coffee and .cjsx.

Cypress supports ES6 syntax out of the box, so you can use either of ES6 modules or CommonJS modules. So you can use both import and require for npm packages and relative modules.

Plugin files

Cypress automatically includes the plugins file cypress/plugins/index.js before every single spec file that it runs. This is done purely as a convenience mechanism so that you don’t have to import this file manually for every single spec file.

You can however configure the initial imported file to a different file.

Support file

The index.js file contained in Cypress/support will be included automatically for you. This file will also run before each of your spec file. This is also done purely as a convenience mechanism. This file is the perfect place to put all your reusable behavior such as Custom commands or global overrides that you want to be applied to all of your spec files.

This is typically done inside of a beforeEach block within any of the Cypress/support files as shown below:

'''beforeEach(() => {
  cy.log('This will run before every test in every spec file!!!!!!')
}'''

Writing tests

Cypress is built on Mocha and Chai. It has support for both Chai's BDD and TDD assertion styles. The tests you write in Cypress will adhere to this style most of the time. If you are already family with writing tests in JavaScript, then writing tests in Cypress will be very easy.

Test Structure

The test interface that Cypress borrows from Mocha provides you with describe(), context(), it() and specify().

context() and describe() are identical while specify() and it()  are identical., So you have to choose whatever terminology works best for you.

'''// -- Start: Our Application Code --
function add (x, y) {
  return x + y
}

function subtract (x, y) {
  return x - y
}

function divide (x, y) {
  return x / y
}

function multiply (x, y) {
  return x * y
}
// -- End: Our Application Code --

// -- Start: Our Cypress Tests --
describe('Unit test our math functions', () => {
  context('math', () => {
    it('can add numbers', () => {
      expect(add(4, 5)).to.eq(9)
    })

    it('can subtract numbers', () => {
      expect(subtract(2, 8)).to.eq(-6)
    })

    specify('can divide numbers', () => {
      expect(divide(8, 2)).to.eq(4)
    })

    specify('can multiply numbers', () => {
      expect(multiply(15, 4)).to.eq(60)
    })
  })
})'''

Hooks

Another feature borrowed from Mocha is hooks. Hooks are used to set conditions that you want to run before a set of tests or before each test. They are very useful to clean up conditions after a set of tests or after each test. Here is an example:

'''describe('Hooks', () => {
  before(() => {
    // will run once before all tests in the block
  })

  after(() => {
    // will run once after all tests in the block
  })

  beforeEach(() => {
    // will run before each test in the block
  })

  afterEach(() => {
    // will run after each test in the block
  })
})'''

Here is the order of hook and test execution is as follows:

  • All before() hooks will run once
  • Any beforeEach() hook will run
  • Tests will run
  • Any AfterEach() hook will run
  • All the after() hooks will run

Excluding and Including Testa

If you want to run a specified test or suite, you will have to append .only() to the function. All the nested suites will be executed. This is the recommended way to run a test suite.

'''function fizzbuzz (value) {
  if (value % 3 === 0 && num % 5 === 0) {
    return 'fizzbuzz'
  }

  if (value % 3 === 0) {
    return 'fizz'
  }

  if (num % 5 === 0) {
    return 'buzz'
  }
}
// -- End: Our Application Code --

// -- Start: Our Cypress Tests --
describe('Unit Test FizzBuzz', () => {
  function numsExpectedToEq (arr, expected) {
    // loops through the array of values and
    // ensure they equal what is expected
    arr.forEach((value) => {
      expect(fizzbuzz(value)).to.eq(expected)
    })
  }

  it.only('will return "fizz" when number is multiple of 3', () => {
    numsExpectedToEq([9, 12, 18], 'fizz')
  })

  it('will return "buzz" when number is multiple of 5', () => {
    numsExpectedToEq([10, 20, 25], 'buzz')
  })

  it('will return "fizzbuzz" when number is multiple of both 3 and 5', () => {
    numsExpectedToEq([15, 30, 60], 'fizzbuzz')
  })
})'''

To skip a test or suite, you will need to append a .skip() like so:

'''it.skip('will return "fizz" when number is multiple of 3', () => {
  numsExpectedToEq([9, 12, 18], 'fizz')
})'''

Dynamically Generate Tests

It is possible to generate tests dynamically, here is an example of how it is done:

'''describe('if your app makes use of jQuery', () => {	
  ['mouseover', 'mouseout', 'mouseenter', 'mouseleave'].forEach((event) => {
    it('triggers event: ' + event, () => {
      // if your app makes use of jQuery, then Cypress can trigger a jQuery
      // event that will cause the event callback to fire
      cy
        .get('#with-jquery').invoke('trigger', event)
        .get('#messages').should('contain', 'the event ' + event + 'was fired')
    })
  })
})'''

Assertions Styles

Cypress has support for both the BDD (expect/should)  and the TDD(assert) assertions.

'''it('can add numbers', () => {
  expect(add(1, 4)).to.eq(5)
})

it('can subtract numbers', () => {
  assert.equal(subtract(6, 12), -6, 'these numbers are equal')
})'''

Watching Tests

When you run a test using cypress open, Cypress will watch the filesystem for changes to your spec files. Now when you add or update a test, Cypress will reload it and run all the tests in that spec file.

This will give you a productive development experience because you can add and edit tests as you are implementing a feature and the Cypress user interface will always reflect your latest edits.

What is watched?

Files

  • Configuration file (cypress.json)
  • Cypress.env.json

Folders

  • Integration directory (cypress/integration/ by default)
  • Support directory (cypress/support/ by default)
  • Plugins directory (cypress/plugins/ by default)

The child folders of these folders are equally watched.

What is not watched?

Cypress will not watch your application code, it will not also what the cypress/fixtures folder.

Configuration

You can set watchForFileChanges configuration property to false to disable file watching.