如何使用Jest模拟导入模块中的函数?

时间:2020-01-27 14:34:06

标签: javascript jestjs msal

我有一个名为authProvider.js的模块,当我在api.js中测试我的一个功能时,我想对其进行模拟。 我在玩笑的配置中设置了“ automock”:true

这是我的结构

src
 |-auth
 |  |-__mocks__
 |  |  |-authProvider.js
 |  |-authProvider.js
 |-utils
    |-api.js
    |-api.test.js

这是我到目前为止尝试过的,但是仅在第一个测试用例中获得了成功。我不确定如何在第二个测试用例中设置模拟...

api.test.js

import { mockAuthProvider } from "../auth/__mocks__/authProvider";
import { getDefaultHeaders, getValueIndexByColumnName } from './api';

describe('API utils', () => {
    describe('getDefaultHeaders', () => {
        it('Not authenticated', async () => {
            expect(await getDefaultHeaders()).toEqual({
                'Content-Type': 'application/json'
            });
        });

        it('Authenticated', async () => {
            mockAuthProvider.getAccount.mockImplementationOnce(() =>
                Promise.resolve({user: 'test'})
            );

            const headers = await getDefaultHeaders();

            expect(mockAuthProvider.getAccount).toBeCalled();
            expect(headers).toEqual({
                Authorization: 'Bearer abc123',
                'Content-Type': 'application/json'
            });
        });
    });
});

api.js

import { authProvider } from '../auth/authProvider';
import settings from '../settings';

export async function getDefaultHeaders() {
    const account = await authProvider.getAccount();
    const authenticationParameters = {
        scopes: ['api://' + settings.AD_CLIENT_ID + '/login'],
        redirectUri: window.location.origin + '/auth.html'
    };
    let token;

    if (account) {
        try {
            token = await authProvider.acquireTokenSilent(authenticationParameters);
        } catch (error) {
            token = await authProvider.acquireTokenPopup(authenticationParameters);
        }
    }

    if (token) {
        return {
            Authorization: `Bearer ${token.accessToken}`,
            'Content-Type': 'application/json'
        }
    }
    return {
        'Content-Type': 'application/json'
    }
}

__模拟__ / authProvider.js

const mockAuthProvider = {
    getAccount: jest.fn(),
    acquireTokenSilent: jest.fn(),
    acquireTokenPopup: jest.fn()
};

module.exports = {
    mockAuthProvider
};

错误消息

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

  18 |             const headers = await getDefaultHeaders();
  19 |
> 20 |             expect(mockAuthProvider.getAccount).toBeCalled();
     |                                                 ^

更新

我添加了一个文件来模拟导出auth提供程序的整个模块,但是我认为仍然不是解决它的最佳方法。由于需要按特定顺序指定返回值,因此在多个测试案例中使用它遇到了困难。

是否有更好的方法来解决此问题?

__模拟__ / react-aad-msal.js

import React from 'react';

const errorObj = {
    message: 'Some error'
};

export const mockGetAccount = jest.fn()
    .mockReturnValueOnce(null) // Not authenticated
    .mockReturnValueOnce({user: 'test'}) // Authenticated silent
    .mockReturnValueOnce({user: 'test'}); // Authenticated popup
export const mockAcquireTokenSilent = jest.fn()
    .mockReturnValueOnce({accessToken: 'abc123'}) // Authenticated silent
    .mockRejectedValueOnce(errorObj); // Authenticated popup
export const mockAcquireTokenPopup = jest.fn()
    .mockReturnValueOnce({accessToken: 'abc123'}); // Authenticated popup

export const MsalAuthProvider = jest.fn(() => ({
    getAccount: mockGetAccount,
    acquireTokenSilent: mockAcquireTokenSilent,
    acquireTokenPopup: mockAcquireTokenPopup
}));

export const AuthenticationState = {
    Authenticated: 'Authenticated',
    Unauthenticated: 'Unauthenticated'
};

export const LoginType = {
    Popup: 'popup'
};

export const AuthenticationActions = {
    Initializing: 'Initializing',
    Initialized: 'Initialized',
    AcquiredIdTokenSuccess: 'AcquiredIdTokenSuccess',
    AcquiredAccessTokenSuccess: 'AcquiredAccessTokenSuccess',
    AcquiredAccessTokenError: 'AcquiredAccessTokenError',
    LoginSuccess: 'LoginSuccess',
    LoginError: 'LoginError',
    AcquiredIdTokenError: 'AcquiredIdTokenError',
    LogoutSucc: 'LogoutSucc',
    AuthenticatedStateChanged: 'AuthenticatedStateChanged'
};

export const AzureAD = ({children}) => <div>{children}</div>;

新的api.test.js如下所示,请注意,测试的顺序现在很重要,因为模拟的返回值是固定顺序的。

import { getDefaultHeaders, axiosCreate, getValueIndexByColumnName } from './api';

describe('API utils', () => {
    describe('getDefaultHeaders', () => {
        it('Not authenticated', async () => {
            const headers = await getDefaultHeaders();

            expect(headers).toEqual({
                'Content-Type': 'application/json'
            });
        });

        it('Authenticated silent', async () => {
            const headers = await getDefaultHeaders();

            expect(headers).toEqual({
                Authorization: 'Bearer abc123',
                'Content-Type': 'application/json'
            });
        });

        it('Authenticated popup', async () => {
            const headers = await getDefaultHeaders();

            expect(headers).toEqual({
                Authorization: 'Bearer abc123',
                'Content-Type': 'application/json'
            });
        });
    });

    describe('axiosCreate', () => {
        it('Create axios API base', () => {
            expect(axiosCreate()).toBeTruthy();
        });
    });

    describe('getValueIndexByColumnName', () => {
        it('Invalid input data', () => {
            expect(getValueIndexByColumnName([], null)).toEqual(null);
            expect(getValueIndexByColumnName(['column1'], null)).toEqual(-1);
        });

        it('Valid input data', () => {
            expect(getValueIndexByColumnName(['column1'], 'column')).toEqual(-1);
            expect(getValueIndexByColumnName(['column1'], 'column1')).toEqual(0);
            expect(getValueIndexByColumnName(['column1', 'column2', 'column3'], 'column2')).toEqual(1);
        });
    });
});

0 个答案:

没有答案