Compare The Response To A Fixture

How to make a network request and compare the server response to a fixture file.

Imagine a situation: you need to make an HTTP request from your Cypress test and compare the result to a JSON object stored in a fixture file. There are several ways of writing such test.

Using cy.fixture

We can load the fixture file when needed using cy.fixture command. We can make the request first, then load the fixture file. Since the response body variable is in the lexical scope, we can use it to form the assertion.

1
2
3
4
5
6
7
it('gives a response matching a fixture object', () => {
cy.request('/sale')
.its('body')
.then((body) => {
cy.fixture('sale.json').should('deep.equal', body)
})
})

Tip: always compare the JavaScript objects and arrays by value using the "deep.equal" assertion.

Of course, we can reverse the order and load the fixture before making the request using the cy.request command.

1
2
3
4
5
6
7
it('gives a response matching a fixture object, loads the fixture first', () => {
cy.fixture('sale.json').then((sale) => {
cy.request('/sale')
.its('body')
.should('deep.equal', sale)
})
})

Import the JSON object

Instead of loading the expected response object using cy.fixture, we can simply import or require it, letting the bundler load it for us.

1
2
3
4
import sale from '../fixtures/sale.json'
it('gives a response matching a fixture object loaded using import keyword', () => {
cy.request('/sale').its('body').should('deep.equal', sale)
})

To be honest, this is my favorite way of loading JSON fixtures in my tests.

Alias and test context property

If we load the fixture from a hook, we can alias its value using cy.as command. The aliases are then available as the test context properties of the "this" object in the function () { ... } callbacks.

1
2
3
4
5
6
7
8
9
beforeEach(() => {
cy.fixture('sale.json').as('sale')
})

it('gives a response matching an alias set as a property', function () {
cy.request('/sale')
.its('body')
.should('deep.equal', this.sale)
})

Load a fixture once

All aliases are reset before each test, thus in the example above I use beforeEach hook. This repeats loading the fixture again and again. We can optimize the load a little by using a before plus a local variable.

1
2
3
4
5
6
7
8
9
10
11
12
let loadedSale
before(() => {
cy.fixture('sale.json').then((sale) => {
loadedSale = sale
})
})

it('gives a response matching a fixture loaded once', () => {
cy.request('/sale')
.its('body')
.should('deep.equal', loadedSale)
})

If you really want to use this.alias syntax, you can even set the fixture as an alias from the local variable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let tempSale
before(() => {
cy.fixture('sale.json').then((sale) => {
tempSale = sale
})
})
beforeEach(() => {
cy.wrap(tempSale).as('sale')
})

it('gives a response matching a fixture reset from a local variable', function () {
cy.request('/sale')
.its('body')
.should('deep.equal', this.sale)
})

Use Cypress.env to keep data

We can also load the fixture once using the before hook and throw it into the Cypress.env object.

1
2
3
4
5
6
7
8
9
10
11
before(() => {
cy.fixture('sale.json').then((sale) =>
Cypress.env('sale', sale),
)
})

it('uses sale from Cypress.env object', () => {
cy.request('/sale')
.its('body')
.should('deep.equal', Cypress.env('sale'))
})

Use cypress-data-session

For all more advanced scenarios of loading and caching data, see cypress-data-session plugin. In our case

1
2
3
4
5
6
7
8
9
10
11
12
13
beforeEach(() => {
cy.dataSession({
name: 'sale',
setup() {
return cy.fixture('sale.json')
},
})
})
it('matches the sale', function () {
cy.request('/sale')
.its('body')
.should('deep.equal', this.sale)
})

Which only initializes the data once and keeps it in memory (or even shares it with other specs), and verifies that it is still valid and should not be recreated.

All the fixture and requests tests with the final cypress-data-session command

I think this about covers it for now.

See also