使用Jest/Typescript测试fs库函数 [英] Testing a fs library function with Jest/Typescript

查看:157
本文介绍了使用Jest/Typescript测试fs库函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试测试我编写的库函数(它在我的代码中有效),但是无法通过fs的模拟进行测试.我有一系列用于处理OS的函数,这些函数包含在函数中,以便应用程序的不同部分可以使用相同的调用.

I am trying to test a library function that I have written (it works in my code) but cannot get the testing with the mock of the fs to work. I have a series of functions for working with the OS wrapped in functions so different parts of the application can use the same calls.

我尝试按照这个问题来模拟文件系统,但是它似乎对我不起作用.

I have tried to follow this question with mocking the file system, but it does not seem to work for me.

下面是演示我的问题基础的简短示例:

A short sample to demonstrate the basics of my issue are below:

import * as 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 fs.readFileSync(PathAndFileName).toString();
}

所以现在我正尝试使用Jest测试此功能:

So now I am trying to test this function using Jest:

import { ReadFileContentsSync } from "./read-file-contents-sync";
const fs = require('fs');

describe('Return Mock data to test the function', () => {
    it('should return the test data', () => {
        const TestData:string = 'This is sample Test Data';

// Trying to mock the reading of the file to simply use TestData
        fs.readFileSync = jest.fn();                
        fs.readFileSync.mockReturnValue(TestData);

// Does not need to exist due to mock above     
        const ReadData = ReadFileContentsSync('test-path');
        expect(fs.readFileSync).toHaveBeenCalled();
        expect(ReadData).toBe(TestData);
    });
});

我得到一个例外,该文件不存在,但我希望没有调用fs.readFileSync的实际调用,而是使用了jest.fn()模拟.

I get an exception that the file does not exist, but I would have expected the actual call to fs.readFileSync to not have been called, but the jest.fn() mock to have been used.

ENOENT: no such file or directory, open 'test-path'

我不确定如何进行此模拟吗?

I am not sure how to do this mock?

推荐答案

自从我提到功能/OO/以及对开玩笑的嘲笑的厌恶以来,我觉得我应该在这里填写一些解释.

Since I mentioned about functional / OO / and the dislike of jest mock, I feel like I should fill in some explanation here.

我不反对jest.mock()或任何模拟库(例如sinon). 我以前使用过它们,它们确实可以达到目的,并且是有用的工具. 但是我发现自己大部分时候都不需要它们,并且在使用它们时会有一些权衡.

I'm not against jest.mock() or any mocking library (such as sinon). I have used them before, and they definitely serve their purpose and is a useful tool. But I find myself do not need them for the most part, and there is some tradeoff when using them.

首先让我演示不使用模拟即可实现代码的三种方式.

Let me first demonstrate three ways that the code can be implemented without the use of mock.

第一种方法是实用的,使用context作为第一个参数:

The first way is functional, using a context as the first argument:

// 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:

The second way is to use 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之前完成,但是涉及名称空间的修改有点笨拙):

The third way uses a modified functional style, which requires TypeScript 3.1 (technically you can do that prior to 3.1, but it is just a bit more clumsy involving namespace hack):

// 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());
    });
});

前两种方式提供了更大的灵活性和隔离性,因为每个调用/实例都有自己的依赖关系引用. 这意味着一个测试的模拟"将不会影响另一个测试.

The first two ways provide more flexibility and isolation because each call/instance have their own reference of the dependency. This means there will be no way that the "mock" of one test would affect the other.

第三种方法不会阻止这种情况的发生,但是具有不更改原始功能的签名的优点.

The third way does not prevent that from happening but have the benefit of not changing the signature of the original function.

所有这些的最底层是依赖性管理. 在大多数情况下,程序或代码难以维护,使用或测试是因为它没有为调用上下文提供控制其被调用者依赖性的方法.

The bottom of all these is dependency management. Most of the time a program or code is hard to maintain, use, or test is because it does not provide a way for the calling context to control the dependency of its callee.

依靠模拟库(尤其是像jest.mock()这样强大的模拟系统)可以轻易养成忽略这一重要方面的习惯.

Relying on mocking library (especially a mocking system as powerful as jest.mock()) can easily get to a habit of ignoring this important aspect.

我建议所有人都去看看的一篇不错的文章是Bob叔叔的Clean Architecture: https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html

One nice article I would recommend everyone to check out is Uncle Bob's Clean Architecture: https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html

这篇关于使用Jest/Typescript测试fs库函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆