用笑话和酶模拟反应组件的自定义服务

Mock a custom service with jest and enzyme for a react component

我有一个简单的组件,可以从 api 调用中加载一些用户,我在服务中对此进行了抽象。这是组件:

export const Dashboard: FunctionComponent = () => {
    const [users, setUsers] = useState<IUser[]>([]);
    const [isLoading, setIsLoading] = useState(true);

    const userService: UserService = UserService.get();

    useEffect(() => {
        userService.getUsers()
            .then((data) => {
                console.log(data)
                setIsLoading(false)
                setUsers(data)
            })
            .catch((error) => {
                console.log("Could not load users: ", error);
                setIsLoading(false)
                setUsers([]);
            });
    }, []);

    return (
        isLoading
            ?
            <div data-testid="loading">
                <h4>Loading...</h4>
            </div>
            :
            <div data-testid="users">
                <UserList users={users}/>
            </div>
    );
}

export default Dashboard;

我的服务是这样的:

export class UserService {
    private static INSTANCE = new UserService();

    private constructor() {}

    public static get(): UserService {
        return UserService.INSTANCE;
    }

    public async getUsers(): Promise<IUser[]> {
        const response = await axios.get("api/users");
        return response.data as IUsers[];
    }
}

我将其提取到 .ts 文件中的原因是我计划在另一个组件中重用该服务,并在此处添加其他 api 调用。

所以现在我想为我的仪表板组件编写一个简单的测试,我将 UserService 模拟为 return 一个承诺,然后测试我的 data-testid=users 是否已呈现。

这是我的测试:

configure({adapter: new Adapter()});
describe("User dashboard component", () => {

    let userService: UserService;

    const users = [
        {
            id: "0c8593e8-8fa6-4d40-b555-5ef812477c70",
            name: "John",
            age: 25
        }
    ];

    beforeAll(() => {
        userService = UserService.get();
    });

    test("renders component", () => {
        userService.getUsers = () => {
            return Promise.resolve(users);
        };
        
        const dashboard = shallow(<Dashboard />);
        expect(dashboard.find(<Dashboard />)).toBeTruthy();

        expect(dashboard.find('[data-testid="users"]').length).toEqual(1);
        expect(toJson(dashboard)).toMatchSnapshot();
    });

    test("loading", () => {
        const dashboard = shallow(<Dashboard />);
        expect(dashboard.find('[data-testid="loading"]').length).toEqual(1);
        expect(toJson(dashboard)).toMatchSnapshot();
    });
});

我不想模拟 useState 挂钩,但显然我与用户解决 Promise 的部分没有任何作用。

我该如何实现?这里的最佳做法是什么?谢谢!

shallow doesn't support useEffect此时应该用mount代替。

组件是异步呈现的,测试也应该是异步的。通过分配模拟方法是一种不好的做法,因为它们无法恢复,这导致测试 cross-contamination。一个使它异步的承诺应该公开以供链接。如果是间谍,可以通过 Jest 间谍 API.

检索

应该是:

jest.spyOn(userService, 'getUsers').mockImplementation(() => Promise.resolve(users));

const dashboard = mount(<Dashboard />);
expect(userService.getUsers).toBeCalledTimes(1);
await act(async () => {
  await userService.getUsers.mock.results[0].value;
});
...

间谍应该在测试之间恢复和清除,以便测试不会相互影响。