Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mock a dependency's constructor Jest

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

like image 377
Oliver Shaw Avatar asked Dec 02 '17 09:12

Oliver Shaw


People also ask

How do I mock a module in 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.

Is it possible to mock a dependency class in typescript?

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.

How to mock class constructors with constructor arguments?

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?

What is the use of mockimplementation in jest?

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.


3 Answers

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.

like image 90
kimy82 Avatar answered Oct 17 '22 08:10

kimy82


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);
like image 28
Estus Flask Avatar answered Oct 17 '22 08:10

Estus Flask


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());

like image 12
David Avatar answered Oct 17 '22 08:10

David