Mock a dependency's constructor Jest
Solution 1
The problem is how a module is being mocked. As the reference states,
Mocks a module with an auto-mocked version when it is being required. <...> Returns the jest object for chaining.
AWS
is not module object but jest
object, and assigning AWS.CloudFormation
will affect nothing.
Also, it's CloudWatch
in one place and CloudFormation
in another.
Testing framework doesn't require to reinvent mock functions, they are already there. It should be something like:
const AWS = require("aws-sdk");
const fakePutMetricData = jest.fn()
const FakeCloudWatch = jest.fn(() => ({
putMetricData: fakePutMetricData
}));
AWS.CloudWatch = FakeCloudWatch;
And asserted like:
expect(fakePutMetricData).toHaveBeenCalledTimes(1);
Solution 2
Above answer works. However, after some time working with jest I would just use the mockImplementation functionality which is useful for mocking constructors.
Below code could be an example:
import * as AWS from 'aws-sdk';
jest.mock('aws-sdk', ()=> {
return {
CloudWatch : jest.fn().mockImplementation(() => { return {} })
}
});
test('AWS.CloudWatch is called', () => {
new AWS.CloudWatch();
expect(AWS.CloudWatch).toHaveBeenCalledTimes(1);
});
Note that in the example the new CloudWatch() just returns an empty object.
Solution 3
According to the documentation mockImplementation
can also be used to mock class constructors:
// SomeClass.js
module.exports = class SomeClass {
method(a, b) {}
};
// OtherModule.test.js
jest.mock('./SomeClass'); // this happens automatically with automocking
const SomeClass = require('./SomeClass');
const mockMethod= jest.fn();
SomeClass.mockImplementation(() => {
return {
method: mockMethod,
};
});
const some = new SomeClass();
some.method('a', 'b');
console.log('Calls to method: ', mockMethod.mock.calls);
If your class constructor has parameters, you could pass jest.fn()
as an argument (eg. const some = new SomeClass(jest.fn(), jest.fn());
Comments
-
Oliver Shaw almost 2 years
I'm a newbie to Jest. I've managed to mock my own stuff, but seem to be stuck mocking a module. Specifically constructors.
usage.js
const AWS = require("aws-sdk") cw = new AWS.CloudWatch({apiVersion: "2010-08-01"}) ... function myMetrics(params) { cw.putMetricData(params, function(err, data){}) }
I'd like to do something like this in the tests.
const AWS = jest.mock("aws-sdk") class FakeMetrics { constructor() {} putMetricData(foo,callback) { callback(null, "yay!") } } AWS.CloudWatch = jest.fn( (props) => new FakeMetrics())
However when I come to use it in
usage.js
the cw is amockConstructor
not aFakeMetrics
I realise that my approach might be 'less than idiomatic' so I'd be greatful for any pointers.
This is a minimal example https://github.com/ollyjshaw/jest_constructor_so
npm install -g jest
jest
-
Estus Flask over 6 yearsIt's not obvious that jest.mock precedes the evaluation of usage.js. Please, provide stackoverflow.com/help/mcve
-
Oliver Shaw over 6 yearsThanks estus. Added an executable example here github.com/ollyjshaw/jest_constructor_so
-
-
Oliver Shaw over 6 yearsMy confusion was certainly this bit. "AWS is not module object but jest object, and assigning AWS.CloudFormation will affect nothing."
-
David Schumann almost 6 yearsHow would you handle this if
AWS
was a class with static variables? -
Estus Flask almost 6 years@DavidNathan Possibly with jest.spyOn. This depends on your case. It's unclear what are 'static variables'. Do you mean static methods? If you have a specific case in mind, consider asking a question that reflects your problem.
-
David Schumann almost 6 yearsSorry. I meant class variables. I opened a separate question: stackoverflow.com/questions/51027294/…
-
Alejandro Corredor over 3 yearsThis would leak to other code using the sdk right? Say I'm using CognitoIdentityServiceProvider in some other place, my tests for that would fail since the mock is not there. How do you reset this on a per test module basis?
-
Ariane almost 3 yearsCould you give an explicit example where constructor arguments are used to set properties for the class instance? I don't understand how to make it work so the end object has the expected properties. You mention passing jest.fn() as an argument, but where? How does it work?
-
David almost 3 yearsI updated my answer. However, if you need to set properties for the class instance, then mocking is probably not what you want anyway?
-
Ariane almost 3 yearsWell, there's code that needs specific instance properties to exist, and getting the actual class isn't possible because its contents depend on specific Webpack loaders that I couldn't replicate. I ended up creating a central mock in mocks that's an actual amended class instead of a jest.fn(), and then enabling it with a simple jest.mock('path'). Only way I found to both have properties and obey constraints where they need to actually be class instances.
-
J W over 2 years@AlejandroCorredor you can call
jest.resetModules
or one of the other module methods