如何用Flow.js interface模拟Jest?令我惊讶的是,我还没有找到解决这个问题的地方。
我对两者都相当陌生,但是我看到的唯一(未试用)选项是创建一个从接口继承的类,然后模拟实现类。这似乎很麻烦,而且我不认为我可以将实现类(实际上是要模拟的类)放置在Jest期望的__mocks__
文件夹中,但仍然可以获得期望的行为。
有什么建议吗?有没有更合适的模拟工具?
更新
为什么要为接口创建模拟?这段代码旨在将域和实现层完全分开,而域类对所有注入的依赖项都使用Flow接口。我想测试这些域类。理想情况下,使用模拟工具可以使我更轻松,更具表现力地修改模拟服务的行为,并确认正在测试的域类正在对这些模拟服务进行适当的调用。
这是我在这种情况下要测试的类的简化示例。 UpdateResources
是要测试的类,而ResourceServer
和ResourceRepository
是我要模拟并“监视”的服务的接口:
// @flow
import type { ResourceServer } from '../ResourceServer';
import type { ResourceRepository } from '../ResourceRepository';
/**
* Use case for updating resources
*/
export default class UpdateResources {
resourceServer: ResourceServer;
resourceRepository: ResourceRepository;
constructor(resourceServer: ResourceServer, resourceRepository: ResourceRepository) {
this.resourceServer = resourceServer;
this.resourceRepository = resourceRepository;
}
async execute(): Promise<boolean> {
const updatesAvailable = await this.resourceServer.checkForUpdates();
if (updatesAvailable) {
const resources = await this.resourceServer.getResources();
await this.resourceRepository.saveAll(resources);
}
return updatesAvailable;
}
}
解决方案
我所采用的似乎对我而言效果很好的方法是在__mocks__
目录中创建接口的模拟实现,该接口为所有已实现的方法公开jest.fn
对象。然后,我使用new
实例化这些模拟实现,并跳过对jest.mock()
的任何使用。
__ mocks __ / MockResourceServer.js
import type { ResourceServer } from '../ResourceServer';
export default class MockResourceServer implements ResourceServer {
getResources = jest.fn(() => Promise.resolve({}));
checkForUpodates = jest.fn(() => Promise.resolve(true));
}
__ mocks __ / MockResourceRepository.js
import type { ResourceRepository } from '../ResourceRepository';
export default class MockResourceRepository implements ResourceRepository {
saveAll = jest.fn(() => Promise.resolve());
}
__ tests __ / UpdateResources.test.js
import UpdateResources from '../UpdateResources';
import MockResourceRepository from '../../__mocks__/MockResourceRepository';
import MockResourceServer from '../../__mocks__/MockResourceServer';
describe('UpdateResources', () => {
describe('execute()', () => {
const mockResourceServer = new MockResourceServer();
const mockResourceRepository = new MockResourceRepository();
beforeEach(() => {
jest.clearAllMocks();
});
it('should check the ResourceServer for updates', async () => {
const updateResources = new UpdateResources(mockResourceServer, mockResourceRepository);
await updateResources.execute();
expect(mockResourceServer.checkForUpdates).toHaveBeenCalledTimes(1);
});
it('should save to ResourceRepository if updates are available', async () => {
mockResourceServer.load.mockResolvedValue(true);
const updateResources = new UpdateResources(mockResourceServer, mockResourceRepository);
await updateResources.execute();
expect(mockResourceRepository.saveAll).toHaveBeenCalledTimes(1);
});
it('should NOT save to ResourceRepository if NO updates are available', async () => {
mockResourceServer.load.mockResolvedValue(false);
const updateResources = new UpdateResources(mockResourceServer, mockResourceRepository);
await updateResources.execute();
expect(mockResourceRepository.saveAll).not.toHaveBeenCalled();
});
});
});
如果任何人都可以提供任何改进,我就是开放的!
答案 0 :(得分:0)
问题是,您实际上不需要来模拟接口的实现。模拟的目的是“看起来像”真实的事物,但是如果您已经有一个说明真实事物外观的接口,则符合的任何实现都将自动同样地服务以及模拟。实际上,从类型检查器的角度来看,“真实”和“模拟”实现之间没有什么区别。
我个人想做的是创建一个模拟实现,可以通过提供模拟响应来构造它。然后,可以通过在测试案例中直接构造它并提供确切的响应,来在任何测试案例中重用它。也就是说,您可以通过在构建时注入响应来“编写”模拟内容并写上模拟内容。它与您的模拟实现之间的区别在于,如果它没有响应,它将抛出异常并使测试失败。这是我写的一篇文章,显示了这种方法:https://dev.to/yawaramin/interfaces-for-scaling-and-testing-javascript-1daj
使用这种技术,测试用例可能看起来像这样:
it('should save to ResourceRepository if updates are available', async () => {
const updateResources = new UpdateResources(
new MockResourceServer({
checkForUpdates: [true],
getResources: [{}],
}),
new MockResourceRepository({
saveAll: [undefined],
}),
);
const result = await updateResources.execute();
expect(result).toBeTruthy();
});
我喜欢这些模拟游戏,因为所有的响应都是明确的,并向您显示正在发生的调用顺序。