Home > Software design >  Cypress preserving data between tests?
Cypress preserving data between tests?

Time:02-03

So I have an issue, essentially between it() test blocks I have different e2e tests that reference a *.data.ts test data file.

These data files look something like this:

export const Customer: CustomerData = {
    firstName: 'John',
    lastName: 'Doe',
    fullName: 'John Doe',
    phoneNumber: '(111) 222-3333',
}

these consts reference a type which look something like this:

export interface CustomerData {
    firstName?: string
    lastName?: string
    phoneNumber?: string
    fullName?: string
}

Im not super familiar with typescript, but these are the basis of the data test files.

The problem I am having, is in some of these data files I might generate a random string or random number (for various data pieces). I want these to be generated each time between each test HOWEVER cypress seems to be using the previous generated data for each test.

so in the data file I might have a function: randomData: generateRandomString(10).toUpperCase() that generates a random set of 10 characters. And the first test it will generate correctly, but the 2nd test will use the previously generated set of characters.

This causes issues with tests because of duplicate data essentially.

How can I make sure Cypress will not do this? I also run into similar problems where I will insert a property for data for a test within the test for THAT particular test and I don't want it for other tests. But Cypress will keep the property for the next test regardless?

CodePudding user response:

You are looking for the Factory Function concept.

function createPerson(firstName, lastName) {
  return {
    firstName: firstName,
    lastName: lastName,
    getFullName() {
      return firstName   ' '   lastName;
    },
  };
}

You can pass in the optional properties on each call depending on the test requirement

import { createCustomer } from '../support/create-customer.ts'

it('tests new customer', () => {
  const customer = createCustomer()
  ...
})

it('tests existing customer', () => {
  const customer = createCustomer('existing')
  ...
})

support/create-customer.ts

export function createCustomer(type): CustomerData  {

  const customer: CustomerData {
    firstName: 'John',
    lastName: 'Doe',
    fullName: 'John Doe',
    phoneNumber: '(111) 222-3333',
  };

  if (type === 'existing') {
    customer.login = 'abc123'
  }

  return customer
}

export function createProduct(type): ProductData {
  ...
}

CodePudding user response:

Converting your data variables to functions that return variables will cause them to regenerate the non-static variables when assigning value.

// *.data.ts
export const getCustomer: CustomerData = () => {
  return { ... }
};

You can then assign the value to a variable in a beforeEach() block in your tests.

// Cypress spec file
import { getCustomer } from 'foo.data.ts'

describe('Some tests', () => {
  let customerData: CustomerData;
  beforeEach(() => {
    customerData = getCustomer();
  });
});

I believe that the reason that the data is static from one test to the next is that when you import the Customer variable, it will generate the randomized data only on import when the file runs, and not every time the variable is used. By converting the variable to a function that is called on each test execution, you're forcing the randomization to occur before the value is returned from the function.

CodePudding user response:

You may find it easier to apply the randomizer inside each test. That way it's clearer what data is for each particular test scenario.

Using the object spread you can either modify properties of Customer or add new properties to Customer

Something like:

data file

export const Customer: CustomerData = {
    firstName: 'John',
    lastName: 'Doe',
    fullName: 'John Doe',
    phoneNumber: '(111) 222-3333',
}

test

import { Customer } from './customer.data.js'
import { generateRandomString } from './helpers'

it('customer with random firstName', () => {

  const customer = {
    ...Customer,
    firstName: generateRandomString(10).toUpperCase()
  }

  expect(customer.firstName).not.to.eq('John')
})

it('customer with login', () => {

  const customer = {
    ...Customer,
    login: '[email protected]'
  }

  expect(customer.login).to.eq('[email protected]')
})

CodePudding user response:

Like others have said, it's generating the data upon import instead of when you call the property.

To have it regenerate each time you call a property you can use property getters.

// randomCustomer will be used like a property
export const Customers = {
  get randomCustomer(): CustomerData {
    return {
      firstName: getRandomName(),
      lastName: getRandomName(),
      // etc
    }
  }
}

// Usage
console.log(Customers.randomCustomer);
// { firstName: 'Alice', lastName: 'A' }

Then anytime you call a property, it will generate a new random string

import { Customers } from './customer.data.ts';

it('First test - new customer' ,() => {
  console.log(Customers.randomCustomer);
  // { firstName: 'Alice', lastName: 'A' }
});

it('Second test - new customer' ,() => {
  console.log(Customers.randomCustomer);
  // { firstName: 'Bob', lastName: 'B' }
});

Some edge cases

it('Two new customers in one test' ,() => {
  console.log(Customers.randomCustomer);
  console.log(Customers.randomCustomer);
  // { firstName: 'Calvin', lastName: 'C' }
  // { firstName: 'Devin', lastName: 'D' }
});

// You can assign randomCustomer to a variable so it doesn't get regenerated if you need the same object later in the test
it('Save one new customer to re-use' ,() => {
  const customer1 = Customers.randomCustomer;
  console.log(customer1)
  console.log(Customers.randomCustomer);
  console.log(customer1)
  // { firstName: 'Edward', lastName: 'E' }
  // { firstName: 'Frank', lastName: 'F' }
  // { firstName: 'Edward', lastName: 'E' }
});
  • Related