在尝试测试使用 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 pass,它不喜欢 as any
布局,并且更喜欢 Variable:type
表示法。
最佳答案
您的代码永远不会返回 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;
此外,您的 stub 需要抛出以模拟相同的行为。
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
,但我个人认为这是矫枉过正。
通常不鼓励使用类型断言,因为它只是告诉编译器,“嘿,我知道你认为 x
是 X
,但我知道它实际上是 Y
,所以让我们将其视为 Y
” ,所以你基本上失去了类型检查的好处。
但特别是对于 mock 和 stub,没关系,因为你有意识地意识到你在“伪造”它,并且你有测试来备份丢失的类型检查。
第二种方式涉及接口(interface)隔离原则(ISP,The I in SOLID principles)。
这个想法是询问你实际需要什么,而不是获取整个类型/接口(interface)。
AccessFileSync.fs = fs as Pick<typeof fs, 'accessSync'>
您的测试不再需要进行类型断言。
请注意,我必须在这里使用类型断言,因为 X.y: <type> = value
不是有效语法。
我能做到:
const fs2: Pick<typeof fs, 'accessSync'> = fs
AccessFileSync.fs = fs2
但这很愚蠢。
这样做的好处是它更精确,并能密切跟踪您的实际使用情况。
这样做的缺点是有点乏味。我希望控制流分析将来可以自动为我做这件事。 :)
关于typescript - 使用 Jest/Typescript 和虚拟函数测试 fs 库函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52725339/