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 a mockConstructor
not a FakeMetrics
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
Instead of mocking every function, jest helps us mimic the entire module using jest.mock. Create mocks directory into the same path of the file to mock, export the functions, and create the module's name in our case weatherAPI.
Jest is an awesome and easy to use testing framework for JavaScript, but when it comes to TypeScript and mocking (specially to mock typescript classes) it can really become painful as TypeScript is not relaxed about the types as JavaScript is. In this article I am going to show you how to mock a dependency class and its functions.
According to the documentation mockImplementation can also be used to mock class constructors: If your class constructor has parameters, you could pass jest.fn () as an argument (eg. const some = new SomeClass (jest.fn (), jest.fn ()); Could you give an explicit example where constructor arguments are used to set properties for the class instance?
Jest provides a method called mockImplementation that allows you to provide new implementation for a mock that has already been created. This was necessary in our case over just assigning the bunyan module to a new jest.fn () with mock implementation, because we needed access to some constants on the mocked bunyan module in our configuration step.
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.
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);
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());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With