使用Jest/Typescript和虚拟函数测试fs库函数 [英] Testing a fs library function with Jest/Typescript and a dummy function

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

问题描述

在尝试测试利用fs模块的库函数时,在

In trying to test a library function that utilized the fs module, I was given help in this question to better test the functionality, without the mocks, which I agreed with @unional was a better method.

我正在尝试对accessSync方法执行相同的操作,但是它的工作方式不同,需要进行一些更改才能进行测试.

I am trying to do the same with the accessSync method, but it is not working the same way and needs some changes for testing.

我的代码,遵循@unional建议的更改:

My code, following the changes suggested by @unional:

import fs from 'fs';
export function AccessFileSync(PathAndFileName: string):boolean {
    if (PathAndFileName === undefined || PathAndFileName === null || PathAndFileName.length === 0) {
        throw new Error('Missing File Name');
    }
    try {
        AccessFileSync.fs.accessSync(PathAndFileName, fs.constants.F_OK | fs.constants.R_OK);
    } catch {
        return false;
    }
    return true;
}
AccessFileSync.fs = fs;

现在,要尝试对其进行测试,我将:

Now, to try to test it, I would:

describe('Return Mock data to test the function', () => {
    it('should return the test data', () => {
        // mock function
        AccessFileSync.fs = {
            accessSync: () => { return true; }
        } as any;

        const AccessAllowed:boolean = AccessFileSync('test-path');      // Does not need to exist due to mock above
        expect(AccessFileSync.fs.accessSync).toHaveBeenCalled();
        expect(AccessAllowed).toBeTruthy();
    });
});

这确实适用于第一个测试,但是后续测试(更改测试)不会获得新值.例如:

This does work for the first test, but subsequent tests, changing the test, does not get the new value. For instance:

describe('Return Mock data to test the function', () => {
    it('should return the test data', () => {
        // mock function
        AccessFileSync.fs = {
            accessSync: () => { return true; }
        } as any;

        const AccessAllowed:boolean = AccessFileSync('test-path');      // Does not need to exist due to mock above
        expect(AccessFileSync.fs.accessSync).toHaveBeenCalled();
        expect(AccessAllowed).toBeTruthy();
    });
});
describe('Return Mock data to test the function', () => {
    it('should return the test data', () => {
        // mock function
        AccessFileSync.fs = {
            accessSync: () => { return false; }
        } as any;

        const AccessAllowed:boolean = AccessFileSync('test-path');      // Does not need to exist due to mock above
        expect(AccessFileSync.fs.accessSync).toHaveBeenCalled();
        expect(AccessAllowed).toBeFalsy();  // <- This Fails
    });
});

此外,我想使用tslint传递,它不喜欢as any布局,并且更喜欢Variable:type表示法.

Also, I would like to have the tslint pass, which does not like the as any layout, and would prefer the Variable:type notation.

推荐答案

您的代码永远不会返回false:

Your code never returns false:

import fs from 'fs';
export function AccessFileSync(PathAndFileName: string): boolean {
  if (PathAndFileName === undefined || PathAndFileName === null || PathAndFileName.length === 0) {
    throw new Error('Missing File Name');
  }
  try {
    AccessFileSync.fs.accessSync(PathAndFileName, fs.constants.F_OK | fs.constants.R_OK);
  }
  catch {
    return false; // here
  }
  return true;
}
AccessFileSync.fs = fs;

此外,您的存根需要抛出以模拟相同的行为.

Also, your stub needs to throw in order to simulate the same behavior.

describe('Return Mock data to test the function', () => {
  it('should return the test data', () => {
    // mock function
    AccessFileSync.fs = {
      accessSync: () => { throw new Error('try to mimic the actual error') }
    };

    const AccessAllowed: boolean = AccessFileSync('test-path');
    expect(AccessAllowed).toBeFalsy(); 
  });
});

对于皮棉错误,有两种处理方法.

As for the lint error, there are two ways to handle it.

第一个是类型断言,这是您要执行的操作,可以将其强制转换为所需的任何内容,例如as typeof fs,但就我个人而言,我认为这太过分了.

The first one is type assertion, which is what you do, and you can cast it to anything you want, such as as typeof fs, but personally I would think that is overkill.

通常不建议使用类型断言,因为它只告诉编译器,嘿,我知道您以为xX,但是我知道它实际上是Y,因此我们将其视为Y". ,因此您基本上失去了类型检查的好处.

Type assertion should generally be discouraged as it just tells the compiler, "hey, I know you thought x is X, but I know it is actually Y, so let's treat it as Y", so you basically lost the benefit of type checking.

但是专门针对模拟和存根,这是可以的,因为您有意识地意识到自己在伪造"它,并且可以进行测试以备份丢失的类型检查.

But specifically for mock and stub, it is ok because you are consciously aware that you are "faking" it, and you have the test to back up the lost of type checking.

第二种方法涉及接口隔离原理(ISP,SOLID原理中的I).

The second way involves the Interface Segregation Principle (ISP, The I in SOLID principles).

这个想法是要问您真正需要什么,而不是获取整个类型/接口.

The idea is to asks for what you actually need, instead of acquiring the whole type/interface.

AccessFileSync.fs = fs as Pick<typeof fs, 'accessSync'>

您的测试不再需要执行类型声明.

Your test doesn't need to do the type assertion anymore.

请注意,我必须在此处使用类型断言,因为X.y: <type> = value不是有效的语法.

Note that I have to use type assertion here because X.y: <type> = value is not a valid syntax.

我可以这样做:

const fs2: Pick<typeof fs, 'accessSync'> = fs
AccessFileSync.fs = fs2

但这只是愚蠢的.

这样做的好处是它更精确,并且可以精确跟踪您的实际使用情况.

The upside of this is that it is more precise and track closely to what you actually use.

它的缺点是比较乏味.我希望控制流分析将来能自动为我做. :)

The downside of this is that it is a bit more tedious. I wish control flow analysis can do that automatically for me in the future. :)

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

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