mswjs / data

Data modeling and relation library for testing JavaScript applications.

Home Page:https://npm.im/@mswjs/data

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Unable to work with data if referenced from Cypress support file

lavoscore opened this issue · comments

Sorry in advance if this is not the right place to post this issue, but I couldn't find any info about it: I'm having a hard time trying to share my db instance between my Cypress support file and my e2e test files.

// my-test.cy.ts (spec file)

import { db } from 'src/mocks/db'; // <-- reference to my db from test

it('works', () => {
  db.users.create();
  // ...test created data here
})

(db is created as simple as possible with export const db = factory({ user: ... }))

I have support/index.ts import start-worker.ts, which starts my msw worker to intercept my API calls. But unfortunately, db is empty inside the handlers:

// test/cypress/support/start-worker.ts

import { setupWorker, rest } from 'msw';
import { db } from 'src/mocks/db'; // <-- reference to my db from support file

const worker = setupWorker(
  rest.get('/v1/users', (req, res, ctx) => {
    console.log(db.users.count()); // logs "0" 😢 
    //...
  }),
);

Cypress.on('test:before:run:async', async () => {
  await worker.start();
});

But, if I import start-worker.ts directly from my test file, it works. db inside the handlers holds the data created in the test.

// my-test.cy.ts (spec file)

import { db } from 'src/mocks/db'; // <-- reference to my db from test
import 'test/cypress/support/start-worker' // <-- db inside that file will be the same

// ...everything works

Am I getting two dbs here? According to docs, Cypress loads the support file once before the tests, but I fail to see why this is enough to create two separate db instances to the same import. I'd appreciate an explanation on why this should be expected.

Hey, @lavoscore. Thanks for raising this.

Am I getting two dbs here?

Yes, I believe that's the case. Regardless of how Cypress works, you are referencing two difference instances of exported db in your test and in your setup file. This happens because those two files run in different environments:

  • Cypress evaluates the setup file as a part of its bootstraping in Node.js.
  • The E2E test file is evaluated in the browser.

Each surface will require a "new" instance of "db", and that's why it's empty when you attempt to use it in your handlers.

Solution

I recommend sharing instances globally through window. This way both your setup and your tests can access them. This may pose certain challenges depending on what kind of data you're sharing, as, I imagine, Cypress serializes shared references similar to Playwright (there isn't really any other way to persist data between processes unless it's serialized, so you cannot share functions).

I'd do the following:

// test/cypress/support/start-worker.ts
import { rest } from 'msw'
import { db } from 'src/mocks/db'

Cypress.on('window:before:load', win => {
  // Set the "db" instance on the window.
  // From now on, only reference it through the window,
  // not through direct imports.
  win.db = db

  // You may also want to store the worker reference
  // the same way so you could "win.msw.worker.use()"
  // from within your test files.
  win.msw = {
    worker: setupWorker(rest.get('/resource', (req, res, ctx) => {
      return res(ctx.text(win.db.users.count()))
    })
  }
});

You should read on the exact Cypress API to get access to the window and persists values there (sorry, I'm not an active Cypress users, so their API constantly slips through my memory).

Then, in your test you do:

// test/my.e2e.test.js

it('', () => {
  const win = cy.win()
  win.db.users.create({ name: 'John' })
  // ...do requests.
})

What a nice answer! Thanks!

It's a pity not to be able to use support files as "reusable behavior" in this case, but I totally understand why now. I think I'll just repeat my direct imports in my tests for now. It's not DRY, but the alternative is a little bit too much for me at the moment. Maybe I'll switch to your proposed solution in the future though.