What do a Cypress fixture, a cheese pizza, and the number φ (Phi) have in common?
Tags: cypress, testing, automation, qa
Published: 2024-04-27 23:02:12

**It sounds like the beginning of a joke, but it’s no joke! If you’re not convinced, just ask Keanu Reeves.**

---

### ACT 1: EXPOSITION

A lot has been written about the all-mighty constant **Phi** (φ = 1.6180339887…), also known as the _Golden Ratio_ or the _Divine Proportion_! It’s the mystic number that represents perfect beauty and keeps showing up everywhere in the known universe.

What about **cheese pizza**? The cheesi-est, umami-est, and yummi-est of all pizzas in the known universe (if you don’t believe it, just ask any kid).

> _**Disclaimer**: I will not delve into the well-known discord over whether that would be a New York cheese pizza or a Chicago cheese pizza; that’s a topic for another day and a different blog._

But do not forget the third pal of these ‘_tres amigos_’: the **Cypress fixture**, which stores our most precious test data for our most valuable automation test scripts, also in the whole known universe.

&nbsp;  
What if we tamper with our divine **Phi** by changing just one of its digits? For example, if we change the first decimal from 6 to 3, the result is 1.3180339887… We’ve just diminished its luster. It is no longer golden neither divine, and has become a mere mundane decimal number like any other.

What might be an even worse profanity… Take your yummy **cheese pizza** and add Canadian bacon and pineapple. What you now have is a Hawaiian pizza! Which to many is probably the greatest blasphemy one can commit in the culinary pizza world. You can pull the pineapple, but it is already ruined, it will taste to pineapple for the rest of the eternity.

> _**Another disclaimer**: Hey, these are not my words, but my kids’. But who am I to argue with them?_

Now, let’s semantically analyze the word ‘fixture’ within the context of **Cypress fixtures**. A fixture is defined as a piece of equipment that is fixed in position. According to Cypress.io documentation, it refers to a fixed set of data located in a file. So, hypothetically speaking, what if we were to change the value of the fixture after loading from the file during our tests? Would it not cease being a fixture and become a variable instead?

---

### ACT 2: CONFRONTATION

There are things in the known universe that we must not (or at least, should not) mess up with! There are things that must be (or at least, should be) immutable: Phi, a cheese pizza, and definitely a Cypress fixture!

Because if a Cypress fixture is mutable, not only might your test be erroneous, but your tests could also overlook a major security issue in your system under test!

&nbsp;  
#### Our Application
You have a backend application that ensures only authorized requests can cancel an existing contract for undertaking a job. There is an API endpoint, **`/contracts/cancel`**, that will cancel the contract if it receives the correct contract number, purchaser’s name, price rate, beneficiary of the job, and one of the two: authorized phone or authorized email.

Our tests will need to verify that the backend logic is implemented correctly and that none of the validations are overlooked.

The record in the contracts table in the database is as follows:

``` sql
contractNumber = "11111"
jobDescription = "Hawaiian pizza slice"
purchaser = "Winston Scott"
contractor = "Caine"
beneficiary = "John Wick"
goldCoinsPriceRate = 3500
authorizedPhone = "310-564-8005"
authorizedEmail = "winston.scott@thehightable.com"
```

&nbsp;  
The cancellation service API is a **`POST`** method for the endpoint **`/contracts/cancel`**:

<p align="center">
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3lqiw1cqcezeurdg9sc0.png" />
</p>

&nbsp;  
And the query the backend developer implemented to retrieve a contract record based on the API request is:

``` sql
SELECT * FROM contract
WHERE contractNumber = @contractNumber
 AND purchaser = @purchaser
 AND beneficiary = @beneficiary
 AND goldCoinsPriceRate = @goldCoinsPriceRate
 AND authorizedPhone = @authorizedPhone
```

&nbsp;  
If the query finds a record that matches the selection, the API will grant the cancellation by returning **`{cancelationSuccess: true}`**; otherwise, it will return **`{cancelationSuccess: false}`**.

> _Notice that the developer **forgot to include the authorizedEmail in the database query**, introducing a defect in the application, right?_

The query should have been instead as follows:

``` sql
SELECT * FROM contract
WHERE contractNumber = @contractNumber
  AND purchaser = @purchaser
  AND beneficiary = @beneficiary
  AND goldCoinsPriceRate = @goldCoinsPriceRate
  AND (authorizedPhone = @authorizedPhone OR authorizedEmail = @authorizedEmail)
```

&nbsp;  
#### Our Tests

For this purpose, you create a fixture that you plan to reuse in many of your tests, containing a set of valid data for some of the fields you need to validate.

Our fixture file is located at **`cypress/fixtures/contract.js`**:

``` json
{
    "contractNumber": "11111",
    "purchaser": "Winston Scott",
    "goldCoinsPriceRate": 3500,
    "beneficiary": "John Wick"
}
```

&nbsp;  
And this is how we implemented our test suite cypress/e2e/contract-cancel.js:

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

import contract from '/cypress/fixtures/contract.js'

describe('Test API /contract/cancel', () => {

    it('Test cancelation with Authorized Phone only', () => {
        contract.authorizedPhone = "310-564-8005"

        cy.request({
            method: 'POST',
            url: '/contract/cancel',
            body: contract,
        })

        // Note - contract object sent in the request contains:
        //   {
        //     "contractNumber": "11111",
        //     "purchaser": "Winston Scott",
        //     "goldCoinsPriceRate": 3500,
        //     "beneficiary": "John Wick",
        //     "authorizedPhone": "310-564-8005"
        //   }

    });

    it('Test cancelation with Authorized Email only', () => {
        contract.authorizedEmail = "winston.scott@thehightable.com"

        cy.request({
            method: 'POST',
            url: '/contract/cancel',
            body: contract,
        })

        // Note - contract object sent in the request ACTUALLY contains:
        //   {
        //     "contractNumber": "11111",
        //     "purchaser": "Winston Scott",
        //     "goldCoinsPriceRate": 3500,
        //     "beneficiary": "John Wick",
        //     "authorizedPhone": "310-564-8005",
        //     "authorizedEmail": "winston.scott@thehightable.com"
        //   }
        // The property "authorizedPhone" still remains from the previous test!!!

    });

});
```

&nbsp;  
The first test (‘_Test cancelation with Authorized Phone only_’), will pass, because the SQL in the backend will find a record for the fields provided in the request body: all valid fields from the fixture, as well as a valid `authorizedPhone`, according to the stored database record.

> _If you pay attention, you will notice that the tester actually modified the imported fixture directly by executing the statement `contract.authorizedPhone = "310-564-8005"` on the global object `contrac`t._

&nbsp;  
For the second test (‘_Test cancelation with Authorized Email only_’), the tester provided an `authorizedEmail` according to the database record, so they expect the test to pass, verifying that the API service is implemented as expected.

And the test indeed pass. However, the real reason the second test passed is that the tester had changed the originally imported fixture in the first test. As a result, for the second test, the contract object still contains `contract.authorizedPhone = "310-564-8005"`, thereby **breaking the principle of test independence**!

&nbsp;  
**But, since all tests passed… LET’S DEPLOY TO PRODUCTION!!!**

And this is when all the troubles begin!

Later, when Winston tries to cancel the contract targeting John using his authorized email (cannot use his authorized phone because he forgot the phone while having a dirty martini with his pals at The High Table), his **`/contracts/cancel`** request will not be successful, and the contract to take care of John is not cancelled.
And we all know what happens when someone tries to go after John, our Divine Proportion (and one of the most formidable forces in the known universe)…

> _**Author’s note**: By the way, Keanu Reeves thinks pineapple belongs on pizza — but only under one specific condition: “if the pineapple is slightly roasted”. So I may need to reconsider my Hawaiian pizza statement at the beginning of the post._ 😉

---

### ACT3: RESOLUTION

Let’s then keep Cypress fixtures immutable, but how can we do that?

We can simply copy the original fixture and then add whatever additional properties we might need to the copy. The easiest way to do this is by using the spread operator `...`, which was introduced in JavaScript in the ES6 version.

The line below will create a new object, `contractToCancel`, that is a copy of the original global object `contract`, with an added property `authorizedPhone`:

``` javascript
const contractToCancel = {...contract, authorizedPhone: "310-564-8005"}
```

So our original spec code would look like something like:

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

import contract from '/cypress/fixtures/contract.js'

describe('Test API /contract/cancel', () => {

    it('Test cancelation with Authorized Phone only', () => {
        const contractToCancel = { ...contract, authorizedPhone: "310-564-8005" }

        cy.request({
            method: 'POST',
            url: '/contract/cancel',
            body: contractToCancel,
        })

        // Note - contractToCancel object sent in the request contains:
        //   {
        //     "contractNumber": "11111",
        //     "purchaser": "Winston Scott",
        //     "goldCoinsPriceRate": 3500,
        //     "beneficiary": "John Wick",
        //     "authorizedPhone": "310-564-8005"
        //   }

    });

    it('Test cancelation with Authorized Email only', () => {
        const contractToCancel = { ...contract, authorizedEmail: "winston.scott@thehightable.com" }

        cy.request({
            method: 'POST',
            url: '/contract/cancel',
            body: contractToCancel,
        })

        // Note - contractToCancel object sent in the request contains:
        //   {
        //     "contractNumber": "11111",
        //     "purchaser": "Winston Scott",
        //     "goldCoinsPriceRate": 3500,
        //     "beneficiary": "John Wick",
        //     "authorizedEmail": "winston.scott@thehightable.com"
        //   }
        // 
    });

});
```

&nbsp;  
Since the copies of contractToCancel are defined within the context of each test, they will not affect other tests because they are destroyed after the test is completed, leaving our original fixture in the global object contract untouched.

#### One last thing

What if you want to remove a property from the original fixture while keeping the original object immutable?

For that, we can use object destructuring, also introduced in ES6.

The line below will create a new object, `newContract`, that is a copy of the original global object `contract`, with the property `beneficiary` removed:

``` javascript
const { beneficiary, newContract } = contract
```

&nbsp;  
Now you know: **do not mess with Cypress fixtures**; they are intended to be just that, fixed assets. Otherwise, you know what pure **mayhem** could ensue in your perfectly beautiful and balanced known universe (AKA tests).

**Don't forget to leave a comment, give a thumbs up, or follow my Cypress blog if you found this post useful.**

Happy reading!