无法多次模拟功能反应,测试

时间:2021-06-05 00:39:26

标签: javascript reactjs jestjs mocking fetch

我想测试我的组件:

const Courses: React.FC = () => {
  const { data, error } = useSWR(
    'some url...',
    fetcher
  );

  console.log(data, error);

  if (error) {
    return (
      <CoursesContainer>
        <Error>Something went wrong.</Error>
      </CoursesContainer>
    );
  }

  if (!data) return <Loader title="loader" />;

  return (
    <CoursesContainer>
      <CollapsibleTable courses={data} />
    </CoursesContainer>
  );
};

export default Courses;

但我不知道为什么我不能模拟它为每个测试返回不同的值。我试过了:

jest.mock('../../utils/fetcher', () => ({
  fetcher: jest
    .fn()
    .mockReturnValue('default')
    .mockReturnValueOnce('first call')
    .mockReturnValueOnce('second call'),
  readData: jest.fn(),
}));

test('Basic render. fetch pending', async () => {
  const component = render(<Courses />);
  await waitFor(() => component.findByTitle('loader'));
  expect(component.baseElement).toMatchSnapshot();
});

test('Basic render, fetch success', async () => {
  const component = render(<Courses />);
  await waitFor(() => component.findByText('CollapsibleTable'));
  expect(component.baseElement).toMatchSnapshot();
});

test('Basic render, fetch error', async () => {
  const component = render(<Courses />);
  await waitFor(() => component.findByText('Something went wrong.'));
  expect(component.baseElement).toMatchSnapshot();
});

而且效果不佳。对于每个测试,只有 first call console.log() - Courses.tsx 中的 console.log(data, error);。 来自玩笑的反馈:

  console.log
    undefined undefined

      at Courses (src/components/Courses.tsx:14:11)

  console.log
    first call undefined

      at Courses (src/components/Courses.tsx:14:11)

  console.log
    first call undefined

      at Courses (src/components/Courses.tsx:14:11)

  console.log
    first call undefined

      at Courses (src/components/Courses.tsx:14:11)

当然,第三个测试 (Basic render, fetch error) 是失败的原因。

我不能改用 spyOn(),我的 fetcher 的 cos 是没有对象的单独函数。

@@更新@@

有我的 fetcherreadData 函数:

const fetcher = (url: string) => {
  return fetch(url)
    .then((response) => response.json())
    .then((data: Array<IFetchData>) => readData(data));
};

const readData = (data: Array<IFetchData>) => {
  let myData: Array<ICourse> = [];

  [ there are some simple operations which create new myData array with 
    properties which I need (there is not any async operations)]

  return myData;
};

2 个答案:

答案 0 :(得分:2)

您还必须为 readData 提供模拟实现。

根据 jest 规范,

<块引用>

我们可以用 jest.fn() 创建一个模拟函数。如果未给出实现,则模拟函数在调用时将返回 undefined。


这将使您的测试更有意义。

 await waitForElementToBeRemoved(() => component.getByTitle('loader'));

我们正在等待加载器标题被删除,以确保标题首先显示,现在在加载器完成时将其删除。

jest.mock('../../utils/fetcher', () => ({
  fetcher: jest
    .fn()
    .mockResolvedValue('default')
    .mockResolvedValueOnce('first call')
    .mockResolvedValueOnce('second call'),
  readData: jest.fn().mockResolvedValue('Read call'), //provide reseolve value
//jest.fn() returns undefined when we dont't provide implementation
}));

test('Basic render. fetch pending', async () => {
  const component = render(<Courses />);
  await waitForElementToBeRemoved(() => component.getByTitle('loader'));
  expect(component.baseElement).toMatchSnapshot();
});

test('Basic render, fetch success', async () => {
  const component = render(<Courses />);
  await waitForElementToBeRemoved(() => component.getByText('CollapsibleTable'));
  expect(component.baseElement).toMatchSnapshot();
});

test('Basic render, fetch error', async () => {
  const component = render(<Courses />);
  await waitForElementToBeRemoved(() => component.getByText('Something went wrong.'));
  expect(component.baseElement).toMatchSnapshot();
});

@更新的答案

<块引用> <块引用>

很抱歉,您无法实现您想要的。原因是渲染函数在您的测试用例中仅被调用一次,因此这意味着 fetcherreadData API 将仅调用一次。

const mockFn = jest.fn();
jest.mock('../../utils/fetcher', () => ({
  fetcher: mockFn.mockResolvedValueOnce('first call'),
  readData: mockFn.mockResolvedValue(['Read call']), // returns array
}));
test('Basic render. fetch pending', async () => {
  const component = render(<Courses />);
  await waitForElementToBeRemoved(() => component.getByTitle('loader'));
  expect(mockFn).toHaveBeenCalledTimes(1); // test passed
  expect(component.baseElement).toMatchSnapshot();
});

即使您再次提供 mockResolvedValueOnce,它也会给 undefined,因为渲染函数没有机会第二次调用 fetcherreadData 的模拟版本。

答案 1 :(得分:1)

看起来您的 MockReturnValue 链出现故障。默认应该是最后,像这样:

jest.mock('../../utils/fetcher', () => ({
  fetcher: jest
    .fn()
    .mockReturnValueOnce('first call')
    .mockReturnValueOnce('second call')
    .mockReturnValue('default'),
  readData: jest.fn(),
}));

从这里查看批准的答案: Jest mock the same function twice with different arguments

相关问题