上下文
我想测试一个自定义钩子,这取决于@react-native-firebase/dynamic-links。我们使用@testing-library来测试钩子(@testing-library/react-hooks)。
这是我想要测试的钩子(这是一个简化的示例):
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(...),但是这对所有的测试都是模拟的。我认为问题在于,我想要模拟的方法,是一个类上的方法。
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导出一个类,但类本身是包装的。
declare const defaultExport: ReactNativeFirebase.FirebaseModuleWithStatics<
FirebaseDynamicLinksTypes.Module,
FirebaseDynamicLinksTypes.Statics
>;根据文档,您需要像下面所描述的那样模拟它。
import dynamicLinks from '@react-native-firebase/dynamic-links';
const dynamicLinksMock = dynamicLinks as jest.MockedClass<typeof dynamicLinks>;但是,它会引发以下错误:
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数量的属性(请参阅下面的代码片段)之后,我放弃了。所以,如果这是要走的路,我想知道如何自动化其中的大部分。
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,这在这种情况下似乎是合适的。因为它将只模拟特定的函数,但是当我试图运行测试时,这会引发运行时错误。
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');
});
});错误:
Cannot spy the getInitialLink property because it is not a function; undefined given instead总之,我完全不知道如何模拟getInitialLink方法。如果有人能提供任何建议或建议,我们将不胜感激!
编辑1:
根据@ Based 275564的建议,我尝试了以下操作:
jest.spyOn(dynamicLinks, 'dynamicLinks').mockImplementation(() => {
return { getInitialLink: () => Promise.resolve('fake-link') };
});不幸的是,由于以下错误,类型记录无法编译:
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中建议的。
发布于 2022-01-22 18:43:30
我更喜欢使用动态链接(或其他防火墙函数)创建服务。很容易被嘲笑。
dynamicLinksService.ts
import dynamicLinks from '@react-native-firebase/dynamic-links';
export const getInitialLink = () => dynamicLinks().getInitialLink();useDynamicLink.ts
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
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');
});
});发布于 2022-01-16 17:20:04
您的jest.spyOn需要一些工作。
Jest.spyOn不同于模拟,因为它在您所处的范围内清理它的模拟(并且在您说显式调用mockImplentation等之前它并不是一个模拟)。因此,它是一个“间谍”。)由于您希望不断地更改模拟,所以应该使用spyOn()并在每个测试中模拟实现,以减少每次清除模拟的样板。虽然两者都可以工作得很好,但我会在第3次尝试中工作。
首先,删除动态链接的模拟,因为我们将监视每个特定的测试,并在那里模拟实现。
第二,因为直接调用导出的函数,所以必须像这样导入和监视函数。
import * as dynamicLinks from '@react-native-firebase/dynamic-links';
const dynamicLinkSpy = jest.spyOn(dynamicLinks, 'dynamicLinks').mockImplentation( ... )dynamicLinks现在是导出的文件jest间谍,它查找的函数是dynamicLinks(),这是生产代码正在调用的。
另一个错误来自于添加.prototype。您应该看看生产代码是如何调用它的,这就是测试应该如何模拟它的方式。同样,要在dynamicLinks上替换实现,就必须创建返回值,该返回值将从对该对象调用的嵌套函数向下工作。此外,由于您使用的是.then(),您的生产代码希望在函数中解决这个问题。就像这样;
const dynamicLinkSpy = jest
.spyOn(dynamicLinks, 'dynamicLinks')
.mockImplementation(()=>{ return {getInitialLink: ()=> Promise.resolve('test')}} );现在,您可以使用不同的返回值,并期待与往常不同的结果。此外,请记住,您应该测试它是否被调用。如下所示:
expect(dynamicLinkSpy).toHaveBeenCalled();https://stackoverflow.com/questions/70712286
复制相似问题