Mock exported function in module - javascript

I have two modules which contain exported functions. "ModuleB" uses a function from "ModuleA". Now I want to test "ModuleB" and mock the used function from "ModuleA".
I use ES6 with babel. For testing I use karma and jasmine.
I tried using babel-rewire and inject-loader, but it just does not work. I'm kind of new to all this and I guess I'm just doing something wrong. So any help is appreciated!
moduleA.js
export function getResult() {
return realResult;
}
moduleB.js
import * as ModuleA from './moduleA'
export function functionToTest() {
let result = ModuleA.getResult();
// do stuff with result which I want to test
}
my test
it("does the right thing", () => {
// I tried using rewire, but this gives me an exception:
ModuleB.__Rewire__('ModuleA', {
getResult: () => {
return fakeResult;
}
});
let xyz = ModuleB.functionToTest(canvas, element);
});

Take a look to this library https://www.npmjs.com/package/mockery I thought It's exactly what do you want. Hope it helps!
EDITED:
Basically you have to use mockery in each test to mock your function, the only problem is that you must use require against import with the modules you want to mock. Look this example:
const moduleUnderTest = './moduleB.js';
const moduleA_fake = { getResult: () => { return "This is a fake result"; } } ;
describe("Mocking functions", () => {
it('Should be Fake Result', (done) => {
mock.registerAllowable(moduleUnderTest);
mock.registerMock('./moduleA.js', moduleA_fake);
mock.enable({ useCleanCache: true });
let ModuleB = require(moduleUnderTest);
let res = ModuleB.functionToTest();
res.should.be.eql("This is a fake result");
mock.disable();
mock.deregisterAll();
done();
});
it('Should be Real Result', (done) => {
let ModuleB = require(moduleUnderTest);
let res = ModuleB.functionToTest();
res.should.be.eql("real foo");
done();
});
});
You could see the complete example Here

For anyone who is interested in how it works with babel-rewire: The trick is to use __RewireAPI__.
import * as ModuleB from '../ModuleB'
import {__RewireAPI__ as ModuleBRewire} from '../ModuleA';
it("does the right thing", () => {
let moduleAMock = {
getResult: () => {
return fakeResult;
}
}
ModuleBRewire.__Rewire__('ModuleA', moduleAMock);
let xyz = ModuleB.functionToTest();
});

Related

How to only mock

jest unit test question here. If I am testing function A, and function A uses function B,
import { funcB } = require('./FileB');
const funcA = () => {
const someData = funcB();
}
Does this mean that, in my unit test file testing funcA, in order to mock funcB, do i HAVE to import the function to mock it? which means the file wouldn't use that function but simply there for mocking purposes or do I have to mock the entire module in which funcB lives on and mock the implementation of that single function?
my goal here is, I dont want every single test in my unit test file to have funcB mocked, I only want it mocked on some test but not all while testing funcA.
So if i have 4 test and 3 of them need it mocked but 2 need its actuall implementation, whats a best way to approach this?
One possible solution - use spyOn and mockRestore for following implementation:
export const funcB = () => {
console.log('original implementation of B')
return 42;
}
import { funcB } from './fileb';
export const funcA = () => {
const someData = funcB();
return someData;
}
import * as fileB from "../funcab/fileb";
import { funcA } from "../funcab/filea";
describe('testing 1...2...3...', () => {
const spy = jest.spyOn(fileB, 'funcB');
afterEach(() => {
spy.mockRestore();
})
it('will test with mocked funcB', () => {
spy.mockReturnValue(1);
expect(funcA()).toEqual(1);
});
it('will test without mocked funcB', () => {
expect(funcA()).toEqual(42);
})
})
Here is a post about Jest mock and spy — mockClear vs mockReset vs mockRestore. I hope you'll find it usefull.
Answer for comment - do you mean sth like this?
const mockFuncB = jest.fn();
jest.mock("../funcab/fileb", () => ({
funcB: () => mockFuncB(),
}));
then the test would look like:
it('will test with mocked funcB', () => {
mockFuncB.mockReturnValue(1);
expect(funcA()).toEqual(1);
});

Jest - module exports / deconstructing causing requireActual to not work or not being able to intercept original method for mocking?

I've got a controller that calls two functions and in one test I would like to see if the child function is called and in my second test see if it returns the correct number.
// ./utilities/sayMyName.js
exports.sayMyName = (name) => name;
// ./utilities/doubleNum.js
exports.doubleNum = (num) => num * 2;
// ./utilities/index.js
const { doubleNum } = require("./doubleNum");
const { sayMyName } = require("./sayMyName");
module.exports = {
doubleNum,
sayMyName,
};
// myController.js
const { doubleNum, sayMyName } = require("./utilities");
exports.doubleMyNum = (num, name) => {
const myName = sayMyName(name);
return doubleNum(num);
};
// myController.test.js
const myController = require("./myController");
const utilities = require("./utilities");
jest.mock("./utilities");
describe("doubleNum", () => {
test("should call sayMyName", () => {
myController.doubleMyNum(2, "test name");
expect(utilities.sayMyName).toHaveBeenCalledWith("test name");
});
test("should double my number", () => {
const { doubleNum } = jest.requireActual("./utilities");
expect(myController.doubleMyNum(2, "test name")).toBe(4);
});
});
First test passes however it's the second one that fails because I originally mocked the utilities module, I followed the docs and using jest.requireActual should bring back the original function but it isn't. I did read that mapping the exports like I did in index.js and using deconstructing can cause issues with intercepting a function to mock it. How can I go about getting this to work?
SORTED
After much reading and testing I'd like to think I've sorted the issue by realising that I was trying to change the implementation of function from index.js that was already imported into the myController.js file that contained the function I was testing
So for example if I want to change the implementation of only one of the exported functions from my index.js file I need to mock the module and reimport myController.js for it to have an affect. So this works:
const myController = require("./myController");
const utilities = require("./utilities");
jest.mock("./utilities");
describe("doubleNum", () => {
beforeEach(() => {
jest.resetModules();
});
test("should call sayMyName", () => {
const spy = jest.spyOn(utilities, "sayMyName");
myController.doubleMyNum(2, "test name");
expect(spy).toHaveBeenCalledWith("test name");
});
test("should double my number", () => {
jest.mock("./utilities", () => {
const { sayMyName, doubleNum } = jest.requireActual("./utilities");
return {
sayMyName,
doubleNum,
};
});
// Reimport myController for the actual utilities module to be restored
const myController = require("./myController");
expect(myController.doubleMyNum(2, "test name")).toBe(4);
});
});
Of course this could be simplified had I not used deconstruction in myController.js, hopefully someone else finds this helpful.
I think this might be because you aren’t doing the mocking in a beforeEach or beforeAll block.
Alternatively, you can look at the resetModules example here, and mock or require the module within each test block instead of for all tests: jest.resetModules

Javscript Jest test framework: when is a mock unmocked?

I want to mock a module and a function for a specific test. I have the following:
test("my test", () => {
jest.mock("some_module")
some_module.some_function = jest.fn();
...
})
test("another test", () => {
...
});
My question is, when the test is finished, will both mocks be "unset" so that I can use the real implementation for my next test? Or do I have to explicitly remove all mocks myself?
when the test is finished will all of the mocks be "unset"?
Jest tests are sandboxed on a test-file basis, so ordinarily after all tests in that file have been run, all the mocks would be restored.
However, what you are doing there: some_module.some_function = jest.fn(); is not mocking via Jest's mocking mechanism, it is monkey-patching an imported function. This will not be removed by Jest.
You should be doing something like this instead:
import { some_function } from 'some-module-path';
jest.mock('some-module-path');
test('my test', () => {
...
expect(some_function).toHaveBeenCalled(); // e.g.
}
UPDATE:
After the discussion in comments, here is an example of doing a simple monkey-patch safely in Jest, so that it is restored for subsequent tests in the same file:
// foo.js -----------------------------------
export const foo = () => 'real foo';
// bar.js -----------------------------------
import { foo } from './foo';
export const bar = () => foo();
// bar.test.js ------------------------------
import { bar } from './bar';
import * as fooModule from './foo';
describe('with mocked foo', () => {
let originalFoo;
beforeAll(() => {
// patch it!
originalFoo = fooModule.foo;
fooModule.foo = () => 'mocked foo';
});
afterAll(() => {
// put it back again!
fooModule.foo = originalFoo;
});
test('mocked return value from foo()', () => {
expect(bar()).toEqual('mocked foo');
});
});
describe('with real foo', () => {
test('expect real return value from foo()', () => {
expect(bar()).toEqual('real foo');
});
});
UPDATE 2:
Another alternative, you can mock the dependency and use the original implementation temporarily with jest.requireActual:
import { bar } from './bar';
import { foo } from './foo';
jest.mock('./foo');
foo.mockReturnValue('mocked foo');
const fooModule = jest.requireActual('./foo')
test('mocked return value from foo()', () => {
expect(bar()).toEqual('mocked foo');
});
test('real return value from foo()', () => {
foo.mockImplementation(fooModule.foo);
expect(bar()).toEqual('real foo');
});

How to mock a node module with jest within a class?

I need to mock the DNS node module in a class but I am unsure how to do so as it is enclosed in the class. Here is a sample of what the class looks like...
import { lookup } from 'dns';
class Foo {
// ...
protected async _bar(IP: string) {
// I want to mock "lookup"
await new Promise<undefined>((resolve, reject) => {
lookup(IP, (err, addr) => {
if (err) reject(new Error('DNS Lookup failed for IP_ADDR ' + IP));
resolve();
});
});
// If dns found then return true
return true;
}
// ...
}
I would like to create a test file foo.spec.ts that contains a test similar to the following:
import { Foo } from './Foo';
describe('Foo', () => {
it('Bar Method returns true on success', () => {
const test = new Foo();
expect(test._bar('192.168.1.1')).resolves.toBeTruthy();
});
});
I am unsure how to mock the lookup call within the class Foo given that the class definition is in a separate file from the test itself.
Any help would be appreciated!
The way you are using lookup won't work since it doesn't return a Promise...
...but you can convert it to a version that does return a Promise by using util.promisify.
The code would end up looking something like this:
import { lookup as originalLookup } from 'dns'; // <= import original lookup...
import { promisify } from 'util';
const lookup = promisify(originalLookup); // <= ...and promisify it
export class Foo {
async _bar(IP: string) {
await lookup(IP).catch(err => { throw new Error('Failed'); });
return true;
}
}
You could then mock lookup in your test using jest.mock like this:
import { Foo } from './Foo';
jest.mock('dns', () => ({
lookup: (hostname, callback) => {
hostname === 'example.com' ? callback() : callback('error');
}
}))
describe('Foo', () => {
it('Bar Method returns true on success', async () => {
const test = new Foo();
await expect(test._bar('example.com')).resolves.toBeTruthy(); // Success!
await expect(test._bar('something else')).rejects.toThrowError('Failed'); // Success!
});
});
Note that the mock needs to be created using jest.mock (and not something like jest.spyOn) since calls to jest.mock get hoisted and run first. The mock needs to be in place before Foo.js is imported since the first thing it does is create and store the promisified lookup.
From jest's tutorial,
jest.mock('./sound-player', () => {
return function() {
return {playSoundFile: () => {}};
};
});
so you could do sth like jest.mock('dns', () => ({ ... }));

Jest: restore original module implementation on a manual mock

I have a pretty common testing use case and I am not sure what's the best approach there.
Context
I would like to test a module that depends on a userland dependency. The userland dependency (neat-csv) exports a single function that returns a Promise.
Goal
I want to mock neat-csv's behavior so that it rejects with an error for one single test. Then I want to restore the original module implementation.
AFAIK, I can't use jest.spyOn here as the module exports a single function.
So I thought using manual mocks was appropriated and it works. However I can't figure it out how to restore the original implementation over a manual mock.
Simplified example
For simplicity here's a stripped down version of the module I am trying to test:
'use strict';
const neatCsv = require('neat-csv');
async function convertCsvToJson(apiResponse) {
try {
const result = await neatCsv(apiResponse.body, {
separator: ';'
});
return result;
} catch (parseError) {
throw parseError;
}
}
module.exports = {
convertCsvToJson
};
And here's an attempt of testing that fails on the second test (non mocked version):
'use strict';
let neatCsv = require('neat-csv');
let { convertCsvToJson } = require('./module-under-test.js');
jest.mock('neat-csv', () =>
jest.fn().mockRejectedValueOnce(new Error('Error while parsing'))
);
const csv = 'type;part\nunicorn;horn\nrainbow;pink';
const apiResponse = {
body: csv
};
const rejectionOf = (promise) =>
promise.then(
(value) => {
throw value;
},
(reason) => reason
);
test('mocked version', async () => {
const e = await rejectionOf(convertCsvToJson(apiResponse));
expect(neatCsv).toHaveBeenCalledTimes(1);
expect(e.message).toEqual('Error while parsing');
});
test('non mocked version', async () => {
jest.resetModules();
neatCsv = require('neat-csv');
({ convertCsvToJson } = require('./module-under-test.js'));
const result = await convertCsvToJson(apiResponse);
expect(JSON.stringify(result)).toEqual(
'[{"type":"unicorn","part":"horn"},{"type":"rainbow","part":"pink"}]'
);
});
I am wondering if jest is designed to do such things or if I am going the wrong way and should inject neat-csv instead ?
What would be the idiomatic way of handling this ?
Yes, Jest is designed to do such things.
The API method you are looking for is jest.doMock. It provides a way of mocking modules without the implicit hoisting that happens with jest.mock, allowing you to mock in the scope of tests.
Here is a working example of your test code that shows this:
const csv = 'type;part\nunicorn;horn\nrainbow;pink';
const apiResponse = {
body: csv
};
const rejectionOf = promise =>
promise.then(value => {
throw value;
}, reason => reason);
test('mocked version', async () => {
jest.doMock('neat-csv', () => jest.fn().mockRejectedValueOnce(new Error('Error while parsing')));
const neatCsv = require('neat-csv');
const { convertCsvToJson } = require('./module-under-test.js');
const e = await rejectionOf(convertCsvToJson(apiResponse));
expect(neatCsv).toHaveBeenCalledTimes(1);
expect(e.message).toEqual('Error while parsing');
jest.restoreAllMocks();
});
test('non mocked version', async () => {
const { convertCsvToJson } = require('./module-under-test.js');
const result = await convertCsvToJson(apiResponse);
expect(JSON.stringify(result)).toEqual('[{"type":"unicorn","part":"horn"},{"type":"rainbow","part":"pink"}]');
});

Categories

Resources