javascript - 我如何用玩笑监视第三方功能?
问题描述
我在模拟第三方依赖项时遇到了麻烦。我总是收到这个错误:
无法窥探未定义的属性,因为它不是函数;给定的未定义
以下是此问题的详细信息。首先,这是我正在测试的功能:
文件:src/js/mp_wrapper.js
import { Viewer } from 'third-party';
module.exports = {
createViewer: container => {
if (util.isElement(container)) {
return new Viewer(container);
} else {
throw new Error(
'Invalid Element when attempting to create underlying viewer.',
);
}
},
}
查看我的第三方的源代码,Viewer
非常简单,如下所示:
function Viewer(){
// Doing things
}
Viewer.prototype.foo = function(){
}
module.exports = Viewer;
最后,这是我的测试。
文件:/tests/mp_wrapper.spec.js
import { Viewer } from 'third-party`;
import mp_wrapper from '../src/js/mp_wrapper';
describe('mp_wrapper', () => {
describe('createViewer', () => {
test('returns a new instance of the Viewer class', () => {
const spy = jest.spyOn(Viewer).mockImplementation(() => jest.fn());
// It fails on the line above... -> "Cannot spy the undefined property because it is not a function; undefined given instead"
const testElement = document.createElement(testElement);
let viewer = mp_wrapper.createViewer(testElement);
expect(spy).toHaveBeenCalled();
expect(viewer).toBeInstancecOf(Viewer);
spy.mockRestore();
});
});
});
我如何模拟和监视查看器本身?
我过去做过这个:
const spy = jest.spyOn(Viewer.prototype, 'foo').mockImplementation(() => jest.fn());
我也试过default
没有运气:
const spy = jest.spyOn(Viewer, 'default').mockImplementation(() => jest.fn());
但现在我想监视查看器。
编辑:
这是我的最终解决方案。@brian-lives-outdoors 答案是正确的,但我没有准确描述我的问题。我试图模拟的第三方库稍微复杂一些,因为它导出了一个包含多个构造函数的模块。它看起来像这样:
module.exports = {
Viewer: require('./path/Viewer'),
Foo: require('./foo_path/Foo'),
Bar: require('./bar_path/Bar')
}
然后里面./path/Viewer
就像我之前描述的那样。
这是我的解决方案最终的样子:
import { Viewer } from 'lib';
import mp_wrapper from '../src/js/mp_wrapper';
jest.genMockFromModule('lib');
jest.mock('lib');
describe('mp_wrapper', () => {
describe('createViewer', () => {
test('returns a new instance of the Viewer class and sets the local _viewer property', () => {
const testContainer = document.createElement('div');
const viewer = mp_wrapper.createViewer(testContainer);
expect(Viewer).toHaveBeenCalledWith(testContainer); // Success!
expect(viewer).toBeInstanceOf(Viewer); // Success!
});
});
});
@brian-lives-outdoors 我不明白的是,如果我注释掉jest.mock('lib');
上面的那一行,它就不起作用……为什么?
为什么genMockFromModule
本身还不够?
解决方案
示例代码混合了 ES6 import
/export
语法和 Nodemodule.exports
语法...
...但基于一个看起来像这样的库:
lib.js
function Viewer() { }
Viewer.prototype.foo = function () { }
module.exports = Viewer;
...它会像这样使用:
mp_wrapper.js
import Viewer from './lib'; // <= Babel allows Viewer to be used like an ES6 default export
export const createViewer = container => new Viewer(container);
...并且要监视您,Viewer
您需要在测试中模拟整个库:
mp_wrapper.spec.js
import Viewer from './lib';
import { createViewer } from './mp_wrapper';
jest.mock('./lib', () => jest.fn()); // <= mock the library
test('returns a new instance of the Viewer class', () => {
const viewer = createViewer('the container');
expect(Viewer).toHaveBeenCalledWith('the container'); // Success!
expect(viewer).toBeInstanceOf(Viewer); // Success!
});
请注意,如果该库是 ES6 库,那么您可以default
像这样直接监视导出:
import * as lib from './lib';
const spy = jest.spyOn(lib, 'default'); // <= spy on the default export
...但是由于 Babel 处理 ES6 和非 ES6 代码之间的互操作的方式,如果库不是 ES6,这种方法就不起作用。
编辑:对后续问题的回应
jest.genMockFromModule
生成模块的模拟版本并返回它。
所以例如这个:
const mock = jest.genMockFromModule('lib');
...生成 的模拟版本lib
并将其分配给mock
. 请注意,这并不意味着lib
在测试期间需要时返回模拟。
jest.genMockFromModule
在创建手动模拟时可能很有用:
__mocks__/lib.js
const lib = jest.genMockFromModule('lib'); // <= generate a mock of the module
lib.someFunc.mockReturnValue('some value'); // <= modify it
module.exports = lib; // <= export the modified mock
在您的最终解决方案中,您有以下两行:
jest.genMockFromModule('lib');
jest.mock('lib');
这一行:
jest.genMockFromModule('lib');
...实际上并没有做任何事情,因为它正在生成模块的模拟,但返回的模拟没有被用于任何事情。
这一行:
jest.mock('lib');
...告诉Jest
自动模拟lib
模块,并且是这种情况下唯一需要的行。
推荐阅读
- python - Quicker way to join multiple lists passed as args to a Python function?
- c++ - 输入为空的函数
- angular - 为延迟加载的模块编写单元测试用例
- c# - 无法跟踪 ** 类型的实例,因为它没有主键。只能跟踪具有主键的实体类型
- activerecord - ActiveRecord 查询其中 id 丢弃破折号后的内容
- c++ - 在析构函数调用的哪个点对象不再存在?
- html - 使用 HTML 和 CSS 实现标尺
- python - 将元素移动到数组末尾
- powershell - Powershell 日期计数错误
- r - R - 比较来自不同数据集的两列并获取新数据集