Так как я упомянул о функционале / OO / и неприязни к шутливому макету, я чувствую, что должен заполнить здесь некоторое объяснение.
Я не против 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());
});
});
Третий способ использует модифицированныйфункциональный стиль, который требует TypeScript 3.1 (технически вы можете сделать это до 3.1, но это немного более неуклюже с использованием взлома пространства имен):
// 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());
});
});
Первые два способа обеспечивают большую гибкость и изоляцию, потому что каждыйВызов / экземпляр имеют собственную ссылку на зависимость.Это означает, что «макет» одного теста не повлияет на другой.
Третий способ не предотвращает этого, но имеет преимущество в том, что не меняет сигнатуру исходной функции.
Основой всего этого является управление зависимостями.Большую часть времени программа или код трудны в обслуживании, использовании или тестировании, потому что они не обеспечивают способ вызывающего контекста управлять зависимостью вызываемого пользователя.
Полагаться на ложную библиотеку (особенноСистема насмешек, столь же мощная, как jest.mock()
), легко может привыкнуть к игнорированию этого важного аспекта.
Одна хорошая статья, которую я рекомендую всем проверить, - Чистая архитектура дяди Боба: https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html