既然我提到了functional/OO/和对jest mock的厌恶,我觉得我应该在这里做一些解释。
我不反对
jest.mock()
或任何模仿库(如
sinon
).
但是我发现我自己在大多数情况下都不需要它们,而且在使用它们的时候有一些折衷。
让我首先演示三种不使用mock实现代码的方法。
context
作为第一个论点:
// read-file-contents-sync.ts
import fs from 'fs';
export function ReadFileContentsSync({ fs } = { fs }, PathAndFileName: string): string {
if (PathAndFileName === undefined || PathAndFileName === null || PathAndFileName.length === 0) {
throw new Error('Need a Path and File');
}
return fs.readFileSync(PathAndFileName).toString();
}
// read-file-contents-sync.spec.ts
import { ReadFileContentsSync } from "./read-file-contents-sync";
describe('Return Mock data to test the function', () => {
it('should return the test data', () => {
const TestData:Buffer = new Buffer('This is sample Test Data');
// Trying to mock the reading of the file to simply use TestData
const fs = {
readFileSync: () => TestData
}
// Does not need to exist due to mock above
const ReadData = ReadFileContentsSync({ fs }, 'test-path');
expect(ReadData).toBe(TestData.toString());
});
});
第二种方法是使用OO:
// read-file-contents-sync.ts
import fs from 'fs';
export class FileReader {
fs = fs
ReadFileContentsSync(PathAndFileName: string) {
if (PathAndFileName === undefined || PathAndFileName === null || PathAndFileName.length === 0) {
throw new Error('Need a Path and File');
}
return this.fs.readFileSync(PathAndFileName).toString();
}
}
// read-file-contents-sync.spec.ts
import { FileReader } from "./read-file-contents-sync";
describe('Return Mock data to test the function', () => {
it('should return the test data', () => {
const TestData: Buffer = new Buffer('This is sample Test Data');
const subject = new FileReader()
subject.fs = { readFileSync: () => TestData } as any
// Does not need to exist due to mock above
const ReadData = subject.ReadFileContentsSync('test-path');
expect(ReadData).toBe(TestData.toString());
});
});
// read-file-contents-sync.ts
import fs from 'fs';
export function ReadFileContentsSync(PathAndFileName: string): string {
if (PathAndFileName === undefined || PathAndFileName === null || PathAndFileName.length === 0) {
throw new Error('Need a Path and File');
}
return ReadFileContentsSync.fs.readFileSync(PathAndFileName).toString();
}
ReadFileContentsSync.fs = fs
// read-file-contents-sync.spec.ts
import { ReadFileContentsSync } from "./read-file-contents-sync";
describe('Return Mock data to test the function', () => {
it('should return the test data', () => {
const TestData: Buffer = new Buffer('This is sample Test Data');
// Trying to mock the reading of the file to simply use TestData
ReadFileContentsSync.fs = {
readFileSync: () => TestData
} as any
// Does not need to exist due to mock above
const ReadData = ReadFileContentsSync('test-path');
expect(ReadData).toBe(TestData.toString());
});
});
前两种方法提供了更大的灵活性和隔离性,因为每个调用/实例都有自己的依赖引用。
这意味着一个测试的“模拟”不可能影响另一个测试。
所有这些的底层是依赖关系管理。
依靠模仿库(尤其是像
笑话
)很容易养成忽视这一重要方面的习惯。
有一篇很好的文章我建议大家去看看,那就是Bob叔叔的干净架构:
https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html