笑话模拟工厂不适用于模拟类

Posted

技术标签:

【中文标题】笑话模拟工厂不适用于模拟类【英文标题】:Jest mock factory not working for class mock 【发布时间】:2021-07-07 00:03:05 【问题描述】:

我正在尝试模拟一个服务类来测试一个 React 组件。但是来自 jest.mock 的模块工厂不起作用。

搜索组件:

import React,  useState  from "react";
import SearchService from "../../services/SearchService";

export default function Search() 
  const [searchResults, setSearchResults] = useState([]);

  function doSearch() 
    const service = new SearchService();
    service.search().then(setSearchResults);
  

  return (
    <div className="component-container">
      <div>
        <button onClick=doSearch>search</button>
      </div>
      searchResults.map((result) => (
        <div key=result>result</div>
      ))
    </div>
  );

搜索服务:

export default class SearchService 
  search = function () 
    return new Promise((resolve) => 
      setTimeout(
        () => resolve(["result 1", "result 2", "result 3", "result 4"]),
        1000
      );
    );
  ;

测试文件:

import React from "react";
import  screen, render  from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import  act  from "react-dom/test-utils";
import Search from "../features/search/Search";

jest.mock("../services/SearchService", () => 
  return jest.fn().mockImplementation(() => 
    return  search: jest.fn().mockResolvedValue(["mock result"]) ;
  );
);

test("Search", async () => 
  render(<Search />);
  const button = screen.getByRole("button");
  expect(button).toBeDefined();
  act(() => 
    userEvent.click(button);
  );
  await screen.findByText("mock result");
);

这与Jest documentation example 的结构相同。在上面的代码中,我通过 jest.mock 的模块工厂参数传递了模拟实现。 但它不起作用。当我记录新的 SerchService() 时,我得到“mockConstructor ”,当我运行测试时,它会抛出错误“service.search is not a function”。

当我将测试文件更改为...时

import React from "react";
import  screen, render  from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import  act  from "react-dom/test-utils";
import Search from "../features/search/Search";
import SearchService from "../services/SearchService";

jest.mock("../services/SearchService");

test("Search", async () => 
  SearchService.mockImplementation(() => 
    return  search: jest.fn().mockResolvedValue(["mock result"]) ;
  );
  render(<Search />);
  const button = screen.getByRole("button");
  expect(button).toBeDefined();
  act(() => 
    userEvent.click(button);
  );
  await screen.findByText("mock result");
);

有效... 我有点能理解为什么它以第二种方式工作,我猜这就像使用 jest.spyOn 一样。我无法理解的是为什么它不适用于第一种方法。

我做错了什么?如何使用 jest.mock 模拟模块实现而不在每个测试中调用 .mockImplementation?

【问题讨论】:

为什么服务是一个类开始?只导出一个函数,没有状态。一般来说,事情不应该new他们的合作者,因为那样他们就会变得非常耦合。 嗨@jonrsharpe,这不是一个真正的项目,我正在尝试学习单元测试,所以我创建了这个假服务作为一个类来尝试模拟一个类。 【参考方案1】:

我发现文档有问题,工厂需要返回一个function()(不是箭头函数),所以我把mock改成下面这样就可以了:

jest.mock("../services/SearchService.js", () => 
  return function () 
    return  search: jest.fn().mockResolvedValue(["mock result"]) ;
  ;
);

发现于this post。

【讨论】:

您没有在 OP 中返回箭头,而是返回了 Jest 间谍。 return jest.fn().mockImplementation( 与您发布的内容之间应该没有区别,除非您在某处重置模拟(并且您不应该这样做)。 @EstusFlask,你是说 OP 应该有效吗?当您说“我应该这样做”时,您是在谈论返回 function() 还是重置模拟?我真的不知道正确的方法,我按照文档示例进行操作,但没有成功。 我不能说它是否 100% 可行,但答案中的代码并不比问题中的代码更正确。我的意思是后者,仔细检查测试和 Jest 配置中没有 *reset* 的东西。但是通过返回一个函数,您可以防止自己使用 Jest 间谍,这很有用。

以上是关于笑话模拟工厂不适用于模拟类的主要内容,如果未能解决你的问题,请参考以下文章

使用笑话模拟时的打字稿错误

手动模拟不适用于 Jest

模拟不适用于 DirectoryServices

MPNowPlayingInfoCenter 不适用于模拟器 [关闭]

为啥我的模拟器不适用于 NativeScript?

android模拟位置不适用于locationservices.fusedlocationapi