admin管理员组文章数量:1316020
I am having trouble mocking a third party dependency. I'm always recieving this error:
Cannot spy the undefined property because it is not a function; undefined given instead
Here are the details of this issue. First, this is the function I am testing:
File: 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.',
);
}
},
}
Looking at the source code for my third-party, Viewer
is very simple and looks like this:
function Viewer(){
// Doing things
}
Viewer.prototype.foo = function(){
}
module.exports = Viewer;
And finally, here is my test.
File: /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();
});
});
});
How can I mock & spy on Viewer itself?
I've done this in the past:
const spy = jest.spyOn(Viewer.prototype, 'foo').mockImplementation(() => jest.fn());
I've also tried default
with no luck:
const spy = jest.spyOn(Viewer, 'default').mockImplementation(() => jest.fn());
But now I want to spy on Viewer.
EDIT:
This was my final solution. @brian-lives-outdoors answer is correct, but I didn't describe my problem accurately. The third party library I was trying to mock was slightly more plex because it export a module containing several constructors. It looked like this:
module.exports = {
Viewer: require('./path/Viewer'),
Foo: require('./foo_path/Foo'),
Bar: require('./bar_path/Bar')
}
Then inside ./path/Viewer
is was as I previously described above.
Here's what my solution ended up looking like:
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 What I don't understand is if I ment out the line jest.mock('lib');
above, it doesn't work...Why?
Why isn't genMockFromModule
sufficient by itself?
I am having trouble mocking a third party dependency. I'm always recieving this error:
Cannot spy the undefined property because it is not a function; undefined given instead
Here are the details of this issue. First, this is the function I am testing:
File: 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.',
);
}
},
}
Looking at the source code for my third-party, Viewer
is very simple and looks like this:
function Viewer(){
// Doing things
}
Viewer.prototype.foo = function(){
}
module.exports = Viewer;
And finally, here is my test.
File: /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();
});
});
});
How can I mock & spy on Viewer itself?
I've done this in the past:
const spy = jest.spyOn(Viewer.prototype, 'foo').mockImplementation(() => jest.fn());
I've also tried default
with no luck:
const spy = jest.spyOn(Viewer, 'default').mockImplementation(() => jest.fn());
But now I want to spy on Viewer.
EDIT:
This was my final solution. @brian-lives-outdoors answer is correct, but I didn't describe my problem accurately. The third party library I was trying to mock was slightly more plex because it export a module containing several constructors. It looked like this:
module.exports = {
Viewer: require('./path/Viewer'),
Foo: require('./foo_path/Foo'),
Bar: require('./bar_path/Bar')
}
Then inside ./path/Viewer
is was as I previously described above.
Here's what my solution ended up looking like:
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 What I don't understand is if I ment out the line jest.mock('lib');
above, it doesn't work...Why?
Why isn't genMockFromModule
sufficient by itself?
- @brian-lives-outdoors Any thoughts on this question? You seem to be an expert on mocking dependencies – calbear47 Commented Aug 29, 2019 at 15:02
2 Answers
Reset to default 5The example code mixes ES6 import
/ export
syntax with Node module.exports
syntax...
...but based on a library that looks like this:
lib.js
function Viewer() { }
Viewer.prototype.foo = function () { }
module.exports = Viewer;
...it would be used like this:
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);
...and to spy on Viewer
you would need to mock the entire library in your test:
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!
});
Note that if the library was an ES6 library then you could spy on the default
export directly like this:
import * as lib from './lib';
const spy = jest.spyOn(lib, 'default'); // <= spy on the default export
...but because of the way Babel handles the interop between ES6 and non-ES6 code this approach doesn't work if the library is not ES6.
Edit: response to the follow-up question
jest.genMockFromModule
generates a mocked version of the module and returns it.
So for example this:
const mock = jest.genMockFromModule('lib');
...generates a mocked version of lib
and assigns it to mock
. Note that this does not mean that the mock will be returned when lib
is required during a test.
jest.genMockFromModule
can be useful when creating a manual mock:
__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
In your final solution you have these two lines:
jest.genMockFromModule('lib');
jest.mock('lib');
This line:
jest.genMockFromModule('lib');
...doesn't actually do anything since it is generating a mock of the module but the returned mock isn't being used for anything.
This line:
jest.mock('lib');
...tells Jest
to auto-mock the lib
module and is the only line that is needed in this case.
Here is a solution:
util.js
const util = {
isElement() {}
};
module.exports = util;
View.js
, the third-party module:
function Viewer() {
// Doing things
console.log('new viewer instance');
}
Viewer.prototype.foo = function() {};
module.exports = { Viewer };
my_wrapper.js
:
const { Viewer } = require('./viewer');
const util = require('./util');
module.exports = {
createViewer: container => {
if (util.isElement(container)) {
return new Viewer(container);
} else {
throw new Error('Invalid Element when attempting to create underlying viewer.');
}
}
};
Unit test:
const { Viewer } = require('./viewer');
const my_wrapper = require('./');
const util = require('./util');
jest.mock('./viewer', () => {
return {
Viewer: jest.fn()
};
});
describe('mp_wrapper', () => {
beforeEach(() => {
jest.resetAllMocks();
});
describe('createViewer', () => {
it('t1', () => {
util.isElement = jest.fn().mockReturnValueOnce(true);
let viewer = my_wrapper.createViewer('el');
expect(util.isElement).toBeCalledWith('el');
expect(viewer).toBeInstanceOf(Viewer);
});
it('t2', () => {
util.isElement = jest.fn().mockReturnValueOnce(false);
expect(() => my_wrapper.createViewer('el')).toThrowError(
new Error('Invalid Element when attempting to create underlying viewer.')
);
expect(Viewer).not.toBeCalled();
});
});
});
Unit test result:
PASS src/stackoverflow/57712713/index.spec.js
mp_wrapper
createViewer
✓ t1 (6ms)
✓ t2 (5ms)
----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 50 | 100 | |
index.js | 100 | 100 | 100 | 100 | |
util.js | 100 | 100 | 0 | 100 | |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 4.134s, estimated 9s
本文标签: javascriptHow do I spyOn third party function with jestStack Overflow
版权声明:本文标题:javascript - How do I spyOn third party function with jest? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741988388a2408823.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论