简体   繁体   中英

Testing if a function is called inside another function Jest

I am writing a Redux action to fulfill a unit test a but am having trouble with the mocked function call inside the test. I believe I have mocked completeRegistration correctly, by importing the module it is declared in and then mocking the function from the module reference, but Jest says the function doesn't get called despite the console log statement confirming that the verified.data.success condition is true .

Any ideas what am I doing wrong? Thanks

auth.js

export const verifyEmail = ({
    originalCode,
    confirmCode,
    token
}) => async dispatch => {
    try {
        const body = JSON.stringify({
            originalCode,
            confirmCode,
            registering: true
        });
        const verified = await axios.post("/api/email/verify", body, config);
        if (verified.data.success) {
            console.log("Success!") // logs "Success!"
            completeRegistration(token); // test is to find out if this function gets called
        } else {
            return { msg: "Code did not match, please try again" };
        }
    } catch (err) {
        dispatch({
            type: REGISTER_FAILED
        });
    }
};

auth.test.js

import * as auth from "../../actions/auth";

const originalCompleteReg = auth.completeRegistration;

describe("verifyEmail action", () => {
    let verifyData;
    beforeEach(() => {
        verifyData = {
            originalCode: "1234546",
            confirmCode: "1234546",
            token: "87618g1u8ojb98k3jb12i3kwdbcjwvbbi92ueg29eubv" // fake data :p
        };
        auth.completeRegistration = jest.fn(() => auth.completeRegistration);
        moxios.install();
    });

    afterEach(() => {
        auth.completeRegistration = originalCompleteReg;
        moxios.uninstall();
    });

    it("calls completeRegistration function if successful", async () => {
        moxios.wait(() => {
            const request = moxios.requests.mostRecent();
            request.respondWith({
                status: 200,
                response: { success: true }
            });
        });

        const store = mockStore({ payload: {} });

        await store.dispatch(auth.verifyEmail(verifyData));
        expect(auth.completeRegistration).toBeCalled();
    });
});

Output

verifyEmail action › calls completeRegistration function if successful

    expect(jest.fn()).toBeCalled()

    Expected number of calls: >= 1
    Received number of calls:    0

The problem is that you are not mocking the internal value of completeRegistration used by verifyEmail within the auth module, but the exported value.

When you import a module you get an object with references to the module's functions. If you overwrite a value in the required module, your own reference is overwritten, but the implementation keeps the original reference.

So, in your case, if you call auth.completeRegistration in your test file you will call the mocked version. But when calling auth.verifyEmail (which internally calls completeRegistration ), the completeRegistration it references is not your overwritten version.

I think you should not be testing that your completeRegistration method is being called (after all, that's an implementation detail). Instead, you should check that your method behaves as expected (ie the behaviour completeRegistration has, be it a redirection to another page, perform an additional request, save a cookie, etc). So, you would be mocking the API that is being used in completeRegistration but not the method itself.

That being said, if you want to check that completeRegistration is being called you still have a couple of options.

Use babel rewire plugin

You'll have to install the plugin and add it to your babel configuration. Once you have done so, your test would look like:

import AuthModule from '../../actions/auth';
import * as auth from '../../actions/auth';

describe('verifyEmail action', () => {
    let verifyData;

    beforeEach(() => {
        verifyData = {
            originalCode: '1234546',
            confirmCode: '1234546',
            token: '87618g1u8ojb98k3jb12i3kwdbcjwvbbi92ueg29eubv'
        };
        moxios.install();
    });

    afterEach(() => {
        moxios.uninstall();
    });

    it('calls completeRegistration function if successful', async () => {
        moxios.wait(() => {
            const request = moxios.requests.mostRecent();
            request.respondWith({
                status: 200,
                response: { success: true }
            });
        });

        const mockFn = jest.fn();
        AuthModule.__Rewire__('completeRegistration', mockFn);

        const store = mockStore({ payload: {} });

        await store.dispatch(auth.verifyEmail(verifyData));
        expect(mockFn).toHaveBeenCalled();
        AuthModule.__ResetDependency__('completeRegistration');
    });
});

Separate the functions in different files

You can create a separate module for completeRegistration function. In this way, as your auth.js will have to import the module, you will be able to mock the module using jest.mock capabilities.

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