【问题标题】:Testing a fs library function with Jest/Typescript and a dummy function使用 Jest/Typescript 和虚拟函数测试 fs 库函数
【发布时间】:2019-03-14 11:40:37
【问题描述】:

在尝试测试使用 fs 模块的库函数时,this question 向我提供了帮助,以便更好地测试功能,没有模拟,我同意 @unional 是一种更好的方法。

我正在尝试对 accessSync 方法做同样的事情,但它的工作方式不同,需要进行一些更改以进行测试。

我的代码,遵循@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;

现在,为了测试它,我会:

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 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 表示法。

【问题讨论】:

    标签: typescript testing jestjs


    【解决方案1】:

    您的代码永远不会返回 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;
    

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

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

    对于 lint 错误,有两种处理方式。

    第一个是类型断言,这就是你所做的,你可以将它转换为你想要的任何东西,例如as typeof fs,但我个人认为这是矫枉过正。

    通常不鼓励类型断言,因为它只是告诉编译器,“嘿,我知道你以为 xX,但我知道它实际上是 Y,所以我们把它当作 Y” ,所以你基本上失去了类型检查的好处。

    但特别是对于 mock 和 stub,这没关系,因为您有意识地意识到自己在“伪造”它,并且您有测试来支持丢失的类型检查。

    第二种方式涉及到接口隔离原则(ISP,The I in SOLID原则)。

    这个想法是询问您实际需要什么,而不是获取整个类型/接口。

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

    您的测试不再需要进行类型断言。

    请注意,我必须在这里使用类型断言,因为X.y: &lt;type&gt; = value 不是有效语法。

    我可以这样做:

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

    但这很愚蠢。

    这样做的好处是它更精确,并且与您的实际使用情况密切相关。

    这样做的缺点是有点乏味。我希望将来控制流分析可以为我自动完成。 :)

    【讨论】:

    • 顺便说一下,通常在 JavaScript 中,函数的约定是驼峰式。 :)
    • 而且由于你没有使用 jest mock,你不需要expect(AccessFileSync.fs.accessSync).toHaveBeenCalled();
    • 我们使用不同的大小写来区分我们的代码和标准库调用,所以当查看源代码中的函数时,我们知道它们来自我们的库,而不是 Javascript。
    • 谢谢。事后我看到了。有没有办法修复 tslint 错误,删除原始声明中的任何内容?
    • 可能是因为你的模拟没有投掷。我已经更新了关于 linting 的答案。看看有没有帮助。
    猜你喜欢
    • 2019-03-04
    • 1970-01-01
    • 1970-01-01
    • 2019-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-14
    • 1970-01-01
    相关资源
    最近更新 更多