cypress-io / cypress

Fast, easy and reliable testing for anything that runs in a browser.
https://cypress.io
MIT License
47.02k stars 3.18k forks source link

Stubbing ES modules (imports) does not work for component testing #17048

Closed twhoff closed 3 years ago

twhoff commented 3 years ago

Current behavior

using cy.stub on an ES module does not work as expected, see example code below:

// Person.js
export const getName = () => 'Bob'
// Test which stubs person
import * as person from './Person'

it('Stubs person', () => {
    cy.stub(person, 'getName').callsFake(() => 'Billy')

    console.log(person.getName()) // Expects 'Billy', gets 'Bob'
})

Cypress 7.5.0

jennifer-shehane commented 3 years ago

I'm getting Billy when I run this code. Could you provide a repo that reproduces the issue?

cypress/integration/persons.js

export const getName = () => 'Bob'

cypress/integration/spec.js

// Test which stubs person
import * as person from './person'

it('Stubs person', () => {
  cy.stub(person, 'getName').callsFake(() => 'Billy')

  expect(person.getName()).to.eq('Billy') // Expects 'Billy', gets 'Bob'
})
Screen Shot 2021-06-22 at 12 49 25 PM
Dorious commented 3 years ago

I'm getting the same problem. @jennifer-shehane but do you get the result inside the app that is using person? What configuration do you have? browserify or webpack? I'm using component test runner with webpack dev server (maybe that is correlated). Want to stub library that is outside of my control and doing import * as Grr from 'grr' doesn't do the trick unfortunately.

twhoff commented 3 years ago

Apologies, I forgot to mention this is when unit testing using @cypress/react and with the command yarn cypress run-ct.

For a bit more information, the app is bootstrapped with create-react-app and is not ejected (so webpack under the hood).

Let me know if you need more information.

image

twhoff commented 3 years ago

@jennifer-shehane just wanted to bump this as essentially stubbing / spying does not work in the context of unit testing.

twhoff commented 3 years ago

So just bumping this again - essentially stubbing module imports (import * as Redux from 'react-redux' for example) doesn't work for component tests (yarn cypress run-ct) but DOES work for regular tests (yarn cypress run) - this is a pretty major issue if you ask me, surprised it has been broken for over a year??

twhoff commented 3 years ago

Update - I finally managed to get this working reasonably with component tests (unit testing).

The core concepts are actually in a code example I didn't find until now (my bad): https://github.com/cypress-io/cypress/tree/master/npm/react/cypress/component/advanced/mocking-imports

bvandercar-vt commented 2 years ago

I do not think this is resolved, Cypress should have a viable solution builtin

bvandercar-vt commented 2 years ago

I am getting the error from cypress "ES Modules cannot be stubbed" when I attempt this.

binvb commented 2 years ago

I am getting the error from cypress "ES Modules cannot be stubbed" when I attempt this.

same to me

SIGSTACKFAULT commented 1 year ago

same problem.

for me, it's much easier to stub the function that retrieves values from localStorage than to fill localStorage

mike-plummer commented 1 year ago

To anyone stumbling on this issue the latest information on this topic can be found in #22355

The TL;DR is that ESM mocking is very different than CommonJS mocking - the ESM spec makes it almost impossible to use standard mocking mechanisms which makes things difficult for Cypress (and any other tool that runs in the browser). We have recently released an experimental plugin that can be applied to your test Vite config to enable common mocking scenarios, but it is an early Alpha release so there will likely be hiccups and customization necessary to get it working in your project. The linked issue above has several suggestions for ways to either restructure your code or restructure your tests to mitigate the need for mocking/spying.

dwilt commented 1 year ago

@mike-plummer we've got a NextJS application and are trying to do ESM mocking.. that issue is referencing a vite plugin. Any thoughts for NextJS?

mike-plummer commented 1 year ago

@dwilt It should be relatively simple to update your Next application's Webpack Config to specify the module output format. Have you customized it to output ESM modules? I thought Webpack was still outputting commonjs by default, but maybe that's changing.

Take a look at the output fields - IIRC you can tweak output.chunkFormat and/or output.module to control what module format Webpack publishes. You can either update the base config for your project, or customize it just for your Cypress tests using this pattern via the webpackConfig prop (example references a react project but it should work for next as well)