Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow simple external module mocking in Cypress #26137

Closed
mfanegg opened this issue Mar 16, 2023 · 3 comments
Closed

Allow simple external module mocking in Cypress #26137

mfanegg opened this issue Mar 16, 2023 · 3 comments
Assignees

Comments

@mfanegg
Copy link

mfanegg commented Mar 16, 2023

What would you like?

I would like to be able to mock external modules with Cypress, similar to jest's jest.mock.

Why is this needed?

Let's say I am using React, and I want to assert in my Cypress test that an external module was called, such as one for logging or tracking.


// component file

import { log } from 'some-external-module';

export const App = () => {
  return (
    <div className="App">
      <button onClick={log}>Click me</h1>
    </div>
  );
};

// test file
const mockLog = cy.stub('some-external-module', log) // this isn't currently possible!

it('it description', () => {
  cy.render(<App />);
  cy.findByText(/Click me/).click();
  expect(mockLog).to.be.calledOnce;
});

With Cypress's cy.stub(), this doesn't currently seem possible. If it is, please let me know -- this is a feature we are hurting for.

Other

In this thread, it looks like this issue was brought up before, but was dismissed by @JessicaSachs because module-based mocking:

  1. would hamper a UI test, since modules are often needed for proper renders
  2. can be mocked at the environment level with cy.intercept, if it is a network request
  3. probably some other reasons that I missed

Unfortunately, 1-2 do not apply to the example. The logging example pops up very often in my tests, (1) does not affect the UI at all, and (2) cannot be intercepted because it may not make a network request outside of production environments.

I understand that Cypress may have a fundamental stance against module-based mocking, but if you truly cannot fulfill this feature request, then I am open to suggestions on how we can satisfy the test case above. Surely the answer can't be to not test it -- it's good to at least make sure that the logging library is being called, right?

@mike-plummer mike-plummer self-assigned this Mar 17, 2023
@mike-plummer
Copy link
Contributor

Hi @mfanegg , thanks for using Cypress! You have asked a very complex question - this is sometimes possible, and it depends on the way your app is architected and bundled. From your code example and the referenced discussion I'm going to assume you're using Cypress Component Testing. Unfortunately, there are inherent restrictions with ES Modules that prevent traditional mocking (by design), so if your app is being served using ESM there are limitations with what we're able to do at present. @JessicaSachs' comment in that discussion gives a really good explanation of the differences between Cypress and Jest in this area, and provides a couple of workarounds.

Put simply, a project using Vite or that has customized Webpack to ship cutting-edge ESM may not be able directly mock modules since they are treated as "sealed", and the JS runtime provided by the browser enforces that security model. This means that traditional stubbing tools like Sinon just can't work without workarounds (see Jess' suggestion regarding a wrapper object). We're just starting to look into expanding our ESM support but we're in the early stages so I don't have any timeline at present.

That said, if you're shipping non-ESM code you likely can do exactly what you're asking for right now. I put together a really quick example using create-react-app and was able to cy.stub a function from an external module without any real customization:

src/App.js

import { toInteger } from 'lodash'

const App = () => (
    <div>{toInteger('5.0')}</div>
);

export default App;

src/App.cy.js

import React from 'react'
import App from './App'
import * as lodash from 'lodash'

describe('<App />', () => {
  it('renders', () => {
    cy.stub(lodash, 'toInteger', () => 10).as('toInteger')
    cy.mount(<App />)
  })
})

@mfanegg
Copy link
Author

mfanegg commented Mar 17, 2023

Thanks for looking at this Mike. When I tried the example I unfortunately do get the error, ESModules cannot be stubbed. Looking forward to any updates on ESM support.

@mike-plummer
Copy link
Contributor

@mfanegg I'm going to go ahead and close this out as a duplicate of #22355 so info & updates remain in one place. As pointed out there are some workarounds (see the discussion thread) if this is a serious issue for you at present.

@mike-plummer mike-plummer closed this as not planned Won't fix, can't repro, duplicate, stale Mar 17, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants