在尝试测试使用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
表示法。
答案 0 :(得分: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();
});
});
对于皮棉错误,有两种处理方法。
第一个是类型断言,它是您要做的,您可以将其强制转换为所需的任何内容,例如as typeof fs
,但就我个人而言,我认为这太过分了。
通常不建议使用类型断言,因为它只告诉编译器,“嘿,我知道您以为x
是X
,但我知道它实际上是Y
,所以让我们来对待它作为Y
”,因此您基本上失去了类型检查的好处。
但是专门针对模拟和存根,这是可以的,因为您有意识地意识到自己在“伪造”它,并且可以进行测试以备份丢失的类型检查。
第二种方法涉及接口隔离原则(ISP,SOLID原则中的I)。
这个想法是要问您真正需要什么,而不是获取整个类型/接口。
AccessFileSync.fs = fs as Pick<typeof fs, 'accessSync'>
您的测试不再需要执行类型声明。
请注意,这里我必须使用类型断言,因为X.y: <type> = value
不是有效的语法。
我可以这样做:
const fs2: Pick<typeof fs, 'accessSync'> = fs
AccessFileSync.fs = fs2
但这只是愚蠢的。
这样做的好处是它更精确,并且可以精确跟踪您的实际使用情况。
它的缺点是它比较乏味。我希望控制流分析将来能自动为我做。 :)