简体   繁体   中英

Mocking exported exports in Jest

I am having a problem whereby if I export * from submodule (using ES6 module syntax and babel) I am unable to mock the submodules functions using Jest from the entry point. I wondered if anyone out there could help...

For example given this structure:

+ __tests__
|    |- index.js
+ greeter
|    |- index.js
|    |- submodule.js
|- index.js

And this code:

index.js

import { sayHello } from "./greeter";

export const greet = (name) => sayHello(name);

greeter/index.js

export * from "./submodule.js";

greeter/submodule.js

export const sayHello = (name) => console.log(`Hello, ${name}`);

__tests__/index.js

import { greet } from "../index";
import * as greeter from "../greeter";

describe("greet", () => {
    it("Should delegate the call to greeter.sayHello", () => {
        const name = "John";

        greet(name);
    });
});

This all works fine and when the test runs it passes. Hello, John is printed to the console as expected. The advantage that make this worth it to me is that index.js is completely unaware of the structure of the greeter module, so i can restructure and refactor that code without worrying about my consumers.

The Rub comes when I try and mock out greeter.sayHello ...

__tests__/index.js

import { greet } from "../index.js";
import * as greeter from "../greeter";

greeter.sayHello = jest.fn();

describe("greet", () => {
    it("Should delegate the call to greeter.sayHello", () => {
        const name = "John";

        greet(name);

        expect(greeter.sayHello).toHaveBeenCalledWith(name);
    });
});

Now instead of the test passing as expected - I get an error:

Test suite failed to run

TypeError: Cannot set property sayHello of [object Object] which only has a getter
...(stack trace)

Changing the greeter import in __tests__/index.js to:

import * as greeter from "../greeter/submodule";

Makes the test pass but puts the coupling back in my test code.

Is there another way?

In order to mock a imported method on the file you want to test you need make sure the mock ran before you import your file(index.js), like this:

// import { greet } from "../index.js";   =====> Import on each test case(it)
// import * as greeter from "../greeter"; =====> Mock on each test case(it)

// greeter.sayHello = jest.fn(); =====> Would be not good to do this, it will mess with the entire import and this jest.fn will be share across your tests making it gives you false positives.

describe("greet", () => {
    it("Should delegate the call to greeter.sayHello", () => {
        const name = "John";

        jest.mock('../greeter', () => ({ sayHello: jest.fn() })); // here you mock the import, remember it needs to be before you require your index.js

        const greet = require('../index.js').greet; // require after the mock

        greet(name);

        expect(greeter.sayHello).toHaveBeenCalledWith(name);
    });
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM