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

Cannot mock fs module #20

Open
bkostrowiecki opened this issue Aug 15, 2018 · 3 comments
Open

Cannot mock fs module #20

bkostrowiecki opened this issue Aug 15, 2018 · 3 comments
Labels
enhancement New feature or request

Comments

@bkostrowiecki
Copy link

bkostrowiecki commented Aug 15, 2018

When I try to mock fs module then feature files cannot be found and the jest throws the exception.

 FAIL  features/Create the hierarchy.feature.ts
  ● Test suite failed to run

    Feature file not found (/Users/user/project/features/Create the hierarchy.feature)
      
      at Object.<anonymous>.exports.loadFeature (node_modules/jest-cucumber/dist/src/parsed-feature-loading.js:151:15)
      at Object.<anonymous> (features/Create the hierarchy.feature.ts:5:108)
          at Generator.next (<anonymous>)
          at new Promise (<anonymous>)
          at Generator.next (<anonymous>)
          at <anonymous>
      at process._tickCallback (internal/process/next_tick.js:188:7)

In my opinion, it will be difficult to make it work with current implementation but I think it's worth a mention.

@bencompton bencompton added the enhancement New feature or request label Sep 18, 2018
@stand-sure
Copy link

This approach works for me

  • Skip the import [see below]
  • Call a function that wires the mock, imports the function under test via jest.requireActual, and cleans up.

const whenIRequestIt = () => {
        jest.mock("fs", () => ({
            readFile: jest.fn(
                (
                    path: string,
                    _: { [name: string]: any },
                    callback: CallbackShape
                ) => {
                    const shouldSucceed = path.endsWith(mockFileName);
                    const err = shouldSucceed ? null : "Not found";
                    const data = shouldSucceed ? mockFileContents : undefined;
                    callback(err, data);
                }
            ),
        }));

        const { functionBeingTested } = jest.requireActual( // this REPLACES the import at the top of the file
            "path/to/module"
        );
        const retVal = functionBeingTested (fileName)
            .then((t: string) => (text = t))
            .catch((e: any) => (err = e));

        jest.restoreAllMocks();

        return retVal;
    };

Earlier in the file...


    let fileName: string | undefined;
    let text: string | undefined;
    let err: any;

    beforeEach(() => {
        fileName = undefined;
        text = undefined;
    });

@bencompton
Copy link
Owner

bencompton commented Apr 10, 2021

Here's another perspective for anyone who happens across this thread. I generally prefer to avoid avoid jest.mock myself due its magical properties creating issues like the one in this thread, and also because it's generally considered better design to depend on abstractions instead of concretions, use dependency injection, and all that jazz. While I don't necessarily follow principles like SOLID dogmatically, I have found that using DI--especially with dependencies that manage I/O--really helps with building fast and stable integration tests, and can also have other beneficial effects.

Case in point, I'm currently working on integration tests for the Jest Cucumber library itself, and I refactored a bit so that Jest itself could be dependency injected and replaced with a mock test runner for integration tests. As a result, the integration tests are able to use Jest Cucumber to test itself, which would be really difficult to accomplish using jest.mock . The other benefit of "relying on abstractions instead of concretions" in this case is that it is also possible to inject not just a mock implementation of Jest for testing, but also other test runners aside from Jest. For example, I did a prototype this week that injected Mocha into Jest Cucumber, and actually it worked quite well to run Jest Cucumber tests with Mocha.

As another example, in React apps where I'm leveraging Jest Cucumber, most of my tests have Jest Cucumber testing the react-redux containers, which create a top-level API for the entire app, and then I use another of my libraries, I/O Source to dependency inject either a real HTTP service implementation, or a mock service implementation. Interestingly, this not only facilitates integration tests, but the same mock service implementations and test data can be used to run the app in "mock mode", which is great for demos, and is also great for development, since engineers can get the front-end flow perfected with mock services and data, and then worry about the back-end once the front-end is perfected.

EDIT:

If anyone is interested in the React app architecture I described above, I do have a tutorial that describes it detail how it all works. The tutorial is admittedly opinionated and based around my own OSS libraries, but the same concepts apply when using other libraries.

@rquast
Copy link

rquast commented May 2, 2021

I agree and think DI gives more flexibility for both testing as well as use of code in other frameworks (like what you did with Mocha). However, I removed all the fs functionality from the experiment I did (fs/callsites/glob) because it won't run in a browser for web test runner (which requires its own readFile method to send the data to playwright/selenium/browserstack that runs the modified jest-cucumber code). Maybe providing an interface for loadFeature and loadFeatures and turning most of the code into a core package that gets implemented by jest-cucumber (like what cucumber did with the gherkin package as a monorepo). Also, Mocha has a few differences from Jest - you can't use test.concurrent with Mocha for example, and timeout is implemented differently too. For now I hacked around that by specifying the framework to use (https://github.com/rquast/gherkin-testkit/blob/5d048f413daaf0cddf962194d9ba52feb690bf59/src/feature-definition-creation.ts#L117). If you were to follow the monorepo pattern, another library, say "test-runner-cucumber" or even "mocha-cucumber" could add @jest-cucumber/core as a dependency and implement loadFeature & loadFeatures? This would help you retain the name jest-cucumber without having to change semantics (though it might be a painful move).

EDIT:

Maybe also an interface for getTestFunction so you can inject that to get around the differences?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants