模拟的 `fs.createFileSync` 和 `fs.unlinkSync` 没有被调用
Mocked `fs.createFileSync` and `fs.unlinkSync` are not getting called
我有一个函数可以做很多事情,但其中包括将文件复制到一个特殊目录,用它做一些事情(调用一些东西来与该文件交互而不使用 fs
模块),然后在完成后删除复制的文件。
import { copyFileSync, unlinkSync } from 'fs';
myOtherFunction(path: string) {
...
}
myIOFunction(somePath: string) {
var copyPath = resolve('otherDir/file2.csv');
copyFileSync(somePath, copyPath);
try {
myOtherFunction(copyPath);
} finally {
unlinkFileSync(copyPath);
}
}
export myFunction() {
...
myIOFunction(resolve('file1.csv));
}
因为只有 myFunction()
被导出(这是唯一应该能够直接交互的东西),我必须通过它对 myOtherFunction()
和 myIOFunction()
进行单元测试。其中一部分是 copyFileSync
和 unlinkFileSync
。
我的测试看起来像这样:
import * as fs from 'fs';
import myFunction from './myFile';
...
it("tests something involving input/output", () => {
mockCopyFile = spyOn(fs, 'copyFileSync');
mockUnlinkFile = spyOn(fs, 'unlinkSync');
...
myFunction();
expect(mockCopyFile).toHaveBeenCalledWith(resolve('file1.csv'), resolve('otherDir/file2.csv'));
expect(mockUnlinkFile).toHaveBeenCalled();
...
});
测试失败,错误是 mockCopyFile
和 mockUnlinkFile
都没有被调用。问题是相应的函数 被调用 - 我已经使用调试器逐步完成测试,并且它们执行时没有问题。所以问题一定是间谍没有正确依附自己。
我不知道如何让他们被调用。我试过在被测试的文件中做 import * as fs from 'fs'
和 fs.copyFileSync()
/fs.unlinkFileSync()
。我试过将模拟放在 beforeAll()
函数中。这两种解决方案都没有帮助。我在同一个测试规范中模拟其他几个不直接相关的方法调用,它们都完全按预期工作;只有这个不是,我不明白为什么。
我的 package.json
包含以下依赖项:
"scripts": {
"test": "tsc && jasmine",
},
"devDependencies": {
"@types/jasmine": "^3.5.10",
"@types/node": "^13.7.7",
"@types/pg": "^7.14.3",
"copyfiles": "^2.2.0",
"jasmine": "^3.5.0",
"jasmine-core": "^3.5.0",
"jasmine-ts": "^0.3.0",
"js-yaml": "^3.13.1",
"mock-fs": "^4.11.0",
"morgan": "^1.10.0",
"nodemon": "^2.0.2",
"swagger-ui-express": "^4.1.3",
"ts-node": "^8.7.0",
"typescript": "^3.8.3"
},
"dependencies": {
"@types/express": "^4.17.3",
"chokidar": "^3.3.1",
"cors": "^2.8.5",
"csv-writer": "^1.6.0",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"murmurhash": "0.0.2",
"pg": "^7.18.2",
"pg-format": "^1.0.4",
"winston": "^3.2.1"
}
我的 jasmine.json
看起来像这样:
{
"spec_dir": "dist",
"spec_files": [
"**/*[sS]pec.js"
],
"helpers": [
"helpers/**/*.js"
],
"stopSpecOnExpectationFailure": false,
"random": true
}
和tsconfig
:
{
"compilerOptions": {
"experimentalDecorators": true,
"module": "commonjs",
"esModuleInterop": true,
"target": "es6",
"moduleResolution": "node",
"sourceMap": true,
"outDir": "dist",
"typeRoots": [
"node_modules/@types",
"node_modules/@types/node"
],
},
"lib": [
"es2015"
]
}
Jasmine spyOn
mocking function returns a Spy
class 不代表任何函数调用的对象,但它有关于模拟函数的辅助方法。您必须直接调用 expect
到 fs.<function>
以检查它是否被调用:
import * as fs from 'fs';
import * as path from 'path';
import { myFunction } from '../src/myFunction';
describe('MyFunc', () => {
it("tests something involving input/output", () => {
spyOn(fs, 'copyFileSync');
spyOn(fs, 'unlinkSync');
myFunction();
expect(fs.copyFileSync).toHaveBeenCalledWith(
path.resolve('file1.csv'),
path.resolve('otherDir/file2.csv')
);
expect(fs.unlinkSync).toHaveBeenCalled();
});
});
您可以使用此 GitHub 存储库测试一个简单的重现示例:https://github.com/clytras/fs-jasminte-ts-mocking
git clone https://github.com/clytras/fs-jasminte-ts-mocking.git
cd fs-jasminte-ts-mockin
npm i
npm run test
更新
您似乎在 tsconfig.json
中将 esModuleInterop
设置为 true
。这意味着当您 import * as fs from 'fs'
不会保留 fs
对象的单个实例时。
您可以将 esModuleInterop
设置为 false
并让您的测试通过 toHaveBeenCalled
和 toHaveBeenCalledWith
,但这可能会破坏项目的某些其他功能。您可以在此处 .
阅读更多关于 esModuleInterop
的内容
如果您不想将 esModuleInterop
设置为 false
,那么您必须像在 ES6 Javascript 中那样导入 fs
,如下所示:
import fs from 'fs'; // Use plain default import instead of * as
import path from 'path';
import { myFunction } from '../src/myFunction';
describe('MyFunc', () => {
it("tests something involving input/output", () => {
spyOn(fs, 'copyFileSync');
spyOn(fs, 'unlinkSync');
myFunction();
expect(fs.copyFileSync).toHaveBeenCalledWith(
path.resolve('file1.csv'),
path.resolve('otherDir/file2.csv')
);
expect(fs.unlinkSync).toHaveBeenCalled();
});
});
我还注意到您的配置文件中缺少以下内容:
你必须使用 jasmine-console-reporter
如果你不这样做:
npm i -D jasmine-console-reporter
- 将
package.json
中的 test
脚本更改为 "test": "tsc && jasmine --reporter=jasmine-console-reporter"
在 jasmine.json
中,将 ts-node/register/type-check.js
添加到助手,例如:
{
...
"helpers": [
"helpers/**/*.js",
"../node_modules/ts-node/register/type-check.js"
],
}
现在你的测试应该通过了。
我有一个函数可以做很多事情,但其中包括将文件复制到一个特殊目录,用它做一些事情(调用一些东西来与该文件交互而不使用 fs
模块),然后在完成后删除复制的文件。
import { copyFileSync, unlinkSync } from 'fs';
myOtherFunction(path: string) {
...
}
myIOFunction(somePath: string) {
var copyPath = resolve('otherDir/file2.csv');
copyFileSync(somePath, copyPath);
try {
myOtherFunction(copyPath);
} finally {
unlinkFileSync(copyPath);
}
}
export myFunction() {
...
myIOFunction(resolve('file1.csv));
}
因为只有 myFunction()
被导出(这是唯一应该能够直接交互的东西),我必须通过它对 myOtherFunction()
和 myIOFunction()
进行单元测试。其中一部分是 copyFileSync
和 unlinkFileSync
。
我的测试看起来像这样:
import * as fs from 'fs';
import myFunction from './myFile';
...
it("tests something involving input/output", () => {
mockCopyFile = spyOn(fs, 'copyFileSync');
mockUnlinkFile = spyOn(fs, 'unlinkSync');
...
myFunction();
expect(mockCopyFile).toHaveBeenCalledWith(resolve('file1.csv'), resolve('otherDir/file2.csv'));
expect(mockUnlinkFile).toHaveBeenCalled();
...
});
测试失败,错误是 mockCopyFile
和 mockUnlinkFile
都没有被调用。问题是相应的函数 被调用 - 我已经使用调试器逐步完成测试,并且它们执行时没有问题。所以问题一定是间谍没有正确依附自己。
我不知道如何让他们被调用。我试过在被测试的文件中做 import * as fs from 'fs'
和 fs.copyFileSync()
/fs.unlinkFileSync()
。我试过将模拟放在 beforeAll()
函数中。这两种解决方案都没有帮助。我在同一个测试规范中模拟其他几个不直接相关的方法调用,它们都完全按预期工作;只有这个不是,我不明白为什么。
我的 package.json
包含以下依赖项:
"scripts": {
"test": "tsc && jasmine",
},
"devDependencies": {
"@types/jasmine": "^3.5.10",
"@types/node": "^13.7.7",
"@types/pg": "^7.14.3",
"copyfiles": "^2.2.0",
"jasmine": "^3.5.0",
"jasmine-core": "^3.5.0",
"jasmine-ts": "^0.3.0",
"js-yaml": "^3.13.1",
"mock-fs": "^4.11.0",
"morgan": "^1.10.0",
"nodemon": "^2.0.2",
"swagger-ui-express": "^4.1.3",
"ts-node": "^8.7.0",
"typescript": "^3.8.3"
},
"dependencies": {
"@types/express": "^4.17.3",
"chokidar": "^3.3.1",
"cors": "^2.8.5",
"csv-writer": "^1.6.0",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"murmurhash": "0.0.2",
"pg": "^7.18.2",
"pg-format": "^1.0.4",
"winston": "^3.2.1"
}
我的 jasmine.json
看起来像这样:
{
"spec_dir": "dist",
"spec_files": [
"**/*[sS]pec.js"
],
"helpers": [
"helpers/**/*.js"
],
"stopSpecOnExpectationFailure": false,
"random": true
}
和tsconfig
:
{
"compilerOptions": {
"experimentalDecorators": true,
"module": "commonjs",
"esModuleInterop": true,
"target": "es6",
"moduleResolution": "node",
"sourceMap": true,
"outDir": "dist",
"typeRoots": [
"node_modules/@types",
"node_modules/@types/node"
],
},
"lib": [
"es2015"
]
}
Jasmine spyOn
mocking function returns a Spy
class 不代表任何函数调用的对象,但它有关于模拟函数的辅助方法。您必须直接调用 expect
到 fs.<function>
以检查它是否被调用:
import * as fs from 'fs';
import * as path from 'path';
import { myFunction } from '../src/myFunction';
describe('MyFunc', () => {
it("tests something involving input/output", () => {
spyOn(fs, 'copyFileSync');
spyOn(fs, 'unlinkSync');
myFunction();
expect(fs.copyFileSync).toHaveBeenCalledWith(
path.resolve('file1.csv'),
path.resolve('otherDir/file2.csv')
);
expect(fs.unlinkSync).toHaveBeenCalled();
});
});
您可以使用此 GitHub 存储库测试一个简单的重现示例:https://github.com/clytras/fs-jasminte-ts-mocking
git clone https://github.com/clytras/fs-jasminte-ts-mocking.git
cd fs-jasminte-ts-mockin
npm i
npm run test
更新
您似乎在 tsconfig.json
中将 esModuleInterop
设置为 true
。这意味着当您 import * as fs from 'fs'
不会保留 fs
对象的单个实例时。
您可以将 esModuleInterop
设置为 false
并让您的测试通过 toHaveBeenCalled
和 toHaveBeenCalledWith
,但这可能会破坏项目的某些其他功能。您可以在此处
esModuleInterop
的内容
如果您不想将 esModuleInterop
设置为 false
,那么您必须像在 ES6 Javascript 中那样导入 fs
,如下所示:
import fs from 'fs'; // Use plain default import instead of * as
import path from 'path';
import { myFunction } from '../src/myFunction';
describe('MyFunc', () => {
it("tests something involving input/output", () => {
spyOn(fs, 'copyFileSync');
spyOn(fs, 'unlinkSync');
myFunction();
expect(fs.copyFileSync).toHaveBeenCalledWith(
path.resolve('file1.csv'),
path.resolve('otherDir/file2.csv')
);
expect(fs.unlinkSync).toHaveBeenCalled();
});
});
我还注意到您的配置文件中缺少以下内容:
你必须使用
jasmine-console-reporter
如果你不这样做:npm i -D jasmine-console-reporter
- 将
package.json
中的test
脚本更改为"test": "tsc && jasmine --reporter=jasmine-console-reporter"
在
jasmine.json
中,将ts-node/register/type-check.js
添加到助手,例如:{ ... "helpers": [ "helpers/**/*.js", "../node_modules/ts-node/register/type-check.js" ], }
现在你的测试应该通过了。