首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >类型记录中Jest中默认导出类的模拟方法

类型记录中Jest中默认导出类的模拟方法
EN

Stack Overflow用户
提问于 2022-01-14 14:40:01
回答 2查看 1.8K关注 0票数 2

上下文

我想测试一个自定义钩子,这取决于@react-native-firebase/dynamic-links。我们使用@testing-library来测试钩子(@testing-library/react-hooks)。

这是我想要测试的钩子(这是一个简化的示例):

代码语言:javascript
复制
import { useEffect } from 'react';
import dynamicLinks from '@react-native-firebase/dynamic-links';
import { navigateFromBackground } from '../deeplink';

// Handles dynamic link when app is loaded from closed state.
export const useDynamicLink = (): void => {
  useEffect(() => {
    void dynamicLinks()
      .getInitialLink()
      .then((link) => {
        if (link && link.url) {
          navigateFromBackground(link.url);
        }
      });
  }, []);
};

我希望getInitialLink调用在每个单独的测试中返回一些内容。我已经能够用getInitialLink来模拟jest.mock(...),但是这对所有的测试都是模拟的。我认为问题在于,我想要模拟的方法,是一个类上的方法。

代码语言:javascript
复制
import { useDynamicLink } from './useDynamicLink';
import { renderHook, act } from '@testing-library/react-hooks';
import { navigateFromBackground } from '../deeplink';

jest.mock('../deeplink');
// IMPORTANT: You cannot mock constructors with arrow functions. New cannot be
// called on an arrow function.
jest.mock('@react-native-firebase/dynamic-links', () => {
  return function () {
    return {
      getInitialLink: async () => ({
        url: 'fake-link',
      }),
    };
  };
});

describe('tryParseDynamicLink', () => {
  it('should return null if url is empty', async () => {
    // IMPORTANT: act wrapper is needed so that all events are handled before
    // state is inspected by the test.
    await act(async () => {
      renderHook(() => useDynamicLink());
    });

    expect(navigateFromBackground).toHaveBeenCalledWith('fake-link');
  });
});

尝试

因此,这是可行的,但我无法更改每个测试的返回值。Jest提供了各种各样的模拟依赖关系的方法,但是我无法使它工作。

默认情况下,Firebase导出一个类,但类本身是包装的。

代码语言:javascript
复制
declare const defaultExport: ReactNativeFirebase.FirebaseModuleWithStatics<
  FirebaseDynamicLinksTypes.Module,
  FirebaseDynamicLinksTypes.Statics
>;

根据文档,您需要像下面所描述的那样模拟它。

代码语言:javascript
复制
import dynamicLinks from '@react-native-firebase/dynamic-links';
const dynamicLinksMock = dynamicLinks as jest.MockedClass<typeof dynamicLinks>;

但是,它会引发以下错误:

代码语言:javascript
复制
Type 'FirebaseModuleWithStatics<Module, Statics>' does not satisfy the constraint 'Constructable'.
  Type 'FirebaseModuleWithStatics<Module, Statics>' provides no match for the signature 'new (...args: any[]): any'.

实际上,它不承认它是一个类,因为它是包装的。

然后,我决定通过使用函数(而不是使用箭头函数)来模拟它。使用这种方法,我能够得到更多的信息,但是使用这种方法,我需要提供所有的属性。我尝试了一段时间,但是在添加了X数量的属性(请参阅下面的代码片段)之后,我放弃了。所以,如果这是要走的路,我想知道如何自动化其中的大部分。

代码语言:javascript
复制
import { useDynamicLink } from './useDynamicLink';
import { renderHook, act } from '@testing-library/react-hooks';
import { navigateFromBackground } from '../deeplink';
import dynamicLinks from '@react-native-firebase/dynamic-links';
const dynamicLinksMock = dynamicLinks as jest.MockedFunction<
  typeof dynamicLinks
>;

jest.mock('../deeplink');

describe('tryParseDynamicLink', () => {
  it('should return null if url is empty', async () => {
    // eslint-disable-next-line prefer-arrow-callback
    dynamicLinksMock.mockImplementationOnce(function () {
      return {
        buildLink: jest.fn(),
        buildShortLink: jest.fn(),
        app: {
          options: {
            appId: 'fake-app-id',
            projectId: 'fake-project-id',
          },
          delete: jest.fn(),
          utils: jest.fn(),
          analytics: jest.fn(),
          name: 'fake-name',
          crashlytics: jest.fn(),
          dynamicLinks: jest.fn(),
        },
        onLink: jest.fn(),
        resolveLink: jest.fn(),
        native: jest.fn(),
        emitter: jest.fn(),
        getInitialLink: async () => ({
          minimumAppVersion: '123',
          utmParameters: { 'fake-param': 'fake-value' },
          url: 'fake-link',
        }),
      };
    });

    await act(async () => {
      renderHook(() => useDynamicLink());
    });

    expect(navigateFromBackground).toHaveBeenCalledWith('fake-link');
  });
});

最后一次尝试是使用spyOn,这在这种情况下似乎是合适的。因为它将只模拟特定的函数,但是当我试图运行测试时,这会引发运行时错误。

代码语言:javascript
复制
import { useDynamicLink } from './useDynamicLink';
import { renderHook, act } from '@testing-library/react-hooks';
import { navigateFromBackground } from '../deeplink';
import dynamicLinks from '@react-native-firebase/dynamic-links';

jest.mock('../deeplink');
// Ensure automock
jest.mock('@react-native-firebase/dynamic-links');

describe('tryParseDynamicLink', () => {
  it('should return null if url is empty', async () => {
    jest
      .spyOn(dynamicLinks.prototype, 'getInitialLink')
      .mockImplementationOnce(async () => 'test');

    await act(async () => {
      renderHook(() => useDynamicLink());
    });

    expect(navigateFromBackground).toHaveBeenCalledWith('fake-link');
  });
});

错误:

代码语言:javascript
复制
Cannot spy the getInitialLink property because it is not a function; undefined given instead

总之,我完全不知道如何模拟getInitialLink方法。如果有人能提供任何建议或建议,我们将不胜感激!

编辑1:

根据@ Based 275564的建议,我尝试了以下操作:

代码语言:javascript
复制
jest.spyOn(dynamicLinks, 'dynamicLinks').mockImplementation(() => {
   return { getInitialLink: () => Promise.resolve('fake-link') };
});

不幸的是,由于以下错误,类型记录无法编译:

代码语言:javascript
复制
No overload matches this call.
  Overload 1 of 4, '(object: FirebaseModuleWithStatics<Module, Statics>, method: never): SpyInstance<never, never>', gave the following error.
    Argument of type 'string' is not assignable to parameter of type 'never'.
  Overload 2 of 4, '(object: FirebaseModuleWithStatics<Module, Statics>, method: never): SpyInstance<never, never>', gave the following error.
    Argument of type 'string' is not assignable to parameter of type 'never'.

我只能对该对象提出静态属性,这些属性是:

这就是为什么我选择了dynamicLinks.prototype,这是在这个answer中建议的。

EN

回答 2

Stack Overflow用户

发布于 2022-01-22 18:43:30

我更喜欢使用动态链接(或其他防火墙函数)创建服务。很容易被嘲笑。

dynamicLinksService.ts

代码语言:javascript
复制
import dynamicLinks from '@react-native-firebase/dynamic-links';

export const getInitialLink = () => dynamicLinks().getInitialLink();

useDynamicLink.ts

代码语言:javascript
复制
import { useEffect } from 'react';

import { navigateFromBackground } from '../deeplink';

import { getInitialLink } from './dynamicLinkService';

export const useDynamicLink = (): void => {
  useEffect(() => {
    getInitialLink().then((link) => {
      if (link && link.url) {
        navigateFromBackground(link.url);
      }
    });
  }, []);
};

useDynamicLink.test.ts

代码语言:javascript
复制
import { renderHook, act } from '@testing-library/react-hooks';

import { navigateFromBackground } from '../deeplink';

import { getInitialLink } from './dynamicLinkService';
import { useDynamicLink } from './useDynamicLink';

jest.mock('../deeplink', () => ({
  navigateFromBackground: jest.fn(),
}));

jest.mock('./dynamicLinkService', () => ({
  getInitialLink: jest.fn(),
}));

describe('The useDynamicLink', () => {
  it('should not navigate when link in empty', async () => {
    const getInitialLinkMock = getInitialLink as jest.Mock;

    getInitialLinkMock.mockResolvedValue(null);

    await act(async () => {
      renderHook(() => useDynamicLink());
    });

    expect(navigateFromBackground).not.toHaveBeenCalled();
  });

  it('should navigate when link is exist', async () => {
    const getInitialLinkMock = getInitialLink as jest.Mock;

    getInitialLinkMock.mockResolvedValue({ url: 'www.google.com' });

    await act(async () => {
      renderHook(() => useDynamicLink());
    });

    expect(navigateFromBackground).toHaveBeenCalledWith('www.google.com');
  });
});
票数 2
EN

Stack Overflow用户

发布于 2022-01-16 17:20:04

您的jest.spyOn需要一些工作。

Jest.spyOn不同于模拟,因为它在您所处的范围内清理它的模拟(并且在您说显式调用mockImplentation等之前它并不是一个模拟)。因此,它是一个“间谍”。)由于您希望不断地更改模拟,所以应该使用spyOn()并在每个测试中模拟实现,以减少每次清除模拟的样板。虽然两者都可以工作得很好,但我会在第3次尝试中工作。

首先,删除动态链接的模拟,因为我们将监视每个特定的测试,并在那里模拟实现。

第二,因为直接调用导出的函数,所以必须像这样导入和监视函数。

代码语言:javascript
复制
import * as dynamicLinks from '@react-native-firebase/dynamic-links';

const dynamicLinkSpy = jest.spyOn(dynamicLinks, 'dynamicLinks').mockImplentation( ... )

dynamicLinks现在是导出的文件jest间谍,它查找的函数是dynamicLinks(),这是生产代码正在调用的。

另一个错误来自于添加.prototype。您应该看看生产代码是如何调用它的,这就是测试应该如何模拟它的方式。同样,要在dynamicLinks上替换实现,就必须创建返回值,该返回值将从对该对象调用的嵌套函数向下工作。此外,由于您使用的是.then(),您的生产代码希望在函数中解决这个问题。就像这样;

代码语言:javascript
复制
const dynamicLinkSpy = jest
  .spyOn(dynamicLinks, 'dynamicLinks')
  .mockImplementation(()=>{ return {getInitialLink: ()=> Promise.resolve('test')}} );

现在,您可以使用不同的返回值,并期待与往常不同的结果。此外,请记住,您应该测试它是否被调用。如下所示:

代码语言:javascript
复制
expect(dynamicLinkSpy).toHaveBeenCalled();
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70712286

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档