在 React Native 中模拟 Linking.openURL 它从未被调用过

Mock Linking.openURL in React Native it's never been called

我正在为我的应用编写一些测试,我正在尝试模拟 Linking 模块。我正在使用 jestLinking.canOpenURL 模拟工作正常(toHaveBeenCalled 正在返回 true),但从未调用 openURL 模拟。

    function mockSuccessLinking() {
      const canOpenURL = jest
        .spyOn(Linking, 'canOpenURL')
        .mockImplementation(() => Promise.resolve(true));
      const openURL = jest
        .spyOn(Linking, 'openURL')
        .mockImplementation(() => Promise.resolve(true));

      return { canOpenURL, openURL };
    }

问题是 openURL 没有被调用。

这是测试:

test('should open url when there is a proper app the open it', async () => {
      const { canOpenURL, openURL } = mockSuccessLinking();
      const { result } = renderHook(() =>
         useApplyToJob('https://www.google.com/'),
      );
      const [apply] = result.current;

      // Act
      apply();

      // Assert
      expect(result.current[1].error).toBeNull();
      expect(canOpenURL).toHaveBeenCalled();
      expect(openURL).toHaveBeenCalled();
});

这是被测钩子:

export function useApplyToJob(url) {
  const [error, setError] = useState(null);

  const apply = () => {
    Linking.canOpenURL(url).then(supported => {
      if (supported) {
        Linking.openURL(url);
      } else {
        setError(`Don't know how to open ${url}`);
      }
    });
  };

  return [apply, { error }];
}

给定 canOpenURL returns 承诺,您需要等待异步发生,然后再测试 openURL 是否已被调用。 react-hooks-testing-library 发布了一些 async utils 来帮助解决这个问题。

通常最好使用 waitForNextUpdatewaitForValueToChange 因为它们更能描述测试正在等待什么,但是在成功的情况下你的钩子不会更新任何状态,所以您将需要改用更通用的 waitFor 实用程序:

test('should open url when there is a proper app the open it', async () => {
      const { canOpenURL, openURL } = mockSuccessLinking();
      const { result, waitFor } = renderHook(() =>
         useApplyToJob('https://www.google.com/'),
      );
      const [apply] = result.current;

      // Act
      apply();

      // Assert
      expect(result.current[1].error).toBeNull();
      expect(canOpenURL).toHaveBeenCalled();

      await waitFor(() => {
          expect(openURL).toHaveBeenCalled();
      });
});

作为旁注,不建议解构 result.current 以访问 apply。它现在可能会工作,但在您调用的 apply 正在使用以前渲染的陈旧值之前不需要太多重构。

同样,我建议将 apply() 调用包装在 act 中,即使它现在不更新任何状态。它只会使将来的重构更容易,并在测试错误情况时使测试更加一致(这将需要 act 调用)。

import { renderHook, act } from '@testing-library/react-hooks';

// ...

test('should open url when there is a proper app the open it', async () => {
      const { canOpenURL, openURL } = mockSuccessLinking();
      const { result, waitFor } = renderHook(() =>
         useApplyToJob('https://www.google.com/'),
      );

      // Act
      act(() => {
        result.current[0]();
      });

      // Assert
      expect(result.current[1].error).toBeNull();
      expect(canOpenURL).toHaveBeenCalled();

      await waitFor(() => {
          expect(openURL).toHaveBeenCalled();
      });
});