【问题标题】:Jest to test a class method which has inner function开玩笑测试具有内部功能的类方法
【发布时间】:2021-12-31 06:24:12
【问题描述】:

我正在为我的 .ts 文件编写一次单元测试。我遇到问题并且无法找到解决方案的地方。希望有人能帮我解决它。

问题

在编写单元测试时。我无法测试 profile 的值。在调用名为 getProfile() 的方法之后。

文件设置

Profile.ts

import { getProfileAPI} from "./api";

class ProfileDetails implements IProfileDetails  {

    public profile: string = ''

    constructor() {}

    getProfile = async () => {
        const { data } = await getProfileAPI();
        if (data) {
            this.profile = data
        }
    };
 }

const profileDetail = new ProfileDetails();
export default profileDetail;

Profile.spec.ts

import Profile from './Profile';

describe('Profile', () => {
    it('getProfile', async () => {
        Profile.getProfile = jest.fn();
        await Profile.getProfile();
        expect(Profile.getProfile).toHaveBeenCalled();
    });
});

所以我在这里面临的挑战是,我可以模拟 getProfile 方法。但我无法模拟在 getProfile 方法中调用的 getProfileAPI 函数。

如何模拟在模拟方法中调用的函数(或)是否有任何其他方法可以解决此问题。请帮忙。

提前致谢。

【问题讨论】:

  • 您正在模拟您试图测试它是否被调用的 getProfile 函数,这似乎有点奇怪。为什么不能模拟getProfileAPI 函数?

标签: reactjs typescript unit-testing jestjs


【解决方案1】:

在回答你的问题之前,我可能有一些cmets:

    1. 你的测试是错误的,它所做的只是调用方法然后检查它是否被调用,当然它总是会通过!
    1. 您并不是真的在嘲笑,实际上您正在擦除旧方法,它可能会对其他测试产生一些影响。
    1. 您的方法“getProfile”应该被称为“getAndSetProfile”或“syncProfile”,或者类似的名称,getProfile 让开发人员感到困惑,他会认为它只获取配置文件并返回它。
    1. 我不建议像这样创建和导出 ProfileDetails 的实例,您应该看看使用 typedi 的 DI(依赖注入)。

别忘了:

单元测试意味着“单元”中的任何依赖项都应该是模拟的,您只能测试“单元”内部的逻辑(在您的情况下,是 getProfile 函数或类本身)。

在这里,您正在从另一个未被模拟的服务调用名为“getProfileAPI”的方法,因此您目前也在测试其逻辑。

这个测试应该有效:

Profile.spec.ts

jest.mock('./api', () => ({
  getProfileAPI: jest.fn(),
}));
import { getProfileAPI } from "./api";
import Profile from './Profile';

describe('Profile', () => {
    it('getProfile', async () => {
        await Profile.getProfile();
        expect(getProfileAPI).toHaveBeenCalled();
    });
});

在我们的示例中,Profile.profile 将为空,因为即使我们模拟了 getProfileAPI 方法,我们也没有让它返回任何东西。你可以测试这两种情况:

jest.mock('./api', () => ({
  getProfileAPI: jest.fn(),
}));
import { getProfileAPI } from "./api";
import Profile from './Profile';

const mockGetProfileAPI = getProfileAPI as jest.Mock; // Typescript fix for mocks, else mockResolvedValue method will show an error

describe('Profile', () => {
    describe('getProfile', () => {
        describe('with data', () => {
            const profile = 'TEST_PROFILE';

            beforeEach(() => {
                mockGetProfileAPI.mockResolvedValue({
                    data: profile,
                });
            });

            it('should call getProfileAPI method', async () => {
                await Profile.getProfile();
                expect(mockGetProfileAPI).toHaveBeenCalled(); // Please note that "expect(getProfileAPI).toHaveBeenCalled();" would work
            });

            it('should set profile', async () => {
                await Profile.getProfile();
                expect(Profile.profile).toBe(profile);
            });
        });

        describe.skip('with no data', () => {
            it('should not set profile', async () => {
                await Profile.getProfile();
                expect(Profile.profile).toStrictEqual(''); // the default value
            });
        });
    });
});

注意:我跳过了最后一个测试,因为它不适用于您的情况。 Profile 不会在测试之间重新创建,并且由于它是一个对象,因此它会在每个测试之间保留 Profile.profile 的值(顺便说一句,这有点奇怪)。这是您不应导出该类的新实例的原因之一。

【讨论】:

  • 非常感谢您的回复。它给了我一个从不同维度看待单元测试的新观点。感谢您的帮助。
猜你喜欢
  • 2018-09-25
  • 2020-10-08
  • 2020-12-15
  • 2020-07-04
  • 1970-01-01
  • 2018-02-22
  • 2019-12-19
  • 2018-08-30
  • 1970-01-01
相关资源
最近更新 更多