Jest mocking module that exports a class and functions - javascript

I have a module that exports a class and 2 functions and that module is imported into a file that is being tested.
someFile.js
const {theclass, thefunction} = require("theModule");
const getSomeFileData = () => {
let obj = new theclass();
//some logic
return obj.getData();
}
In the test file, I want to mock the module "theModule" and return a known value when the function obj.getData() is called. How would I go about mocking this module("theModule") when testing file "someFile.js"?

Edit:
.spec.ts
import { someFunction } from './index-test';
jest.mock('lodash', () => {
return {
uniqueId: () => 2,
};
});
describe('', () => {
it('', () => {
expect(someFunction()).toBe(2);
});
});
index-test.ts
import { uniqueId } from 'lodash';
export const someFunction = () => {
return uniqueId();
};

Related

mocking private methods to test an exported method fails in jest

I'm having an actual class like this
my-file.js
const methodA = () => { return 'output-from-methodA'; }
const methodB = () => { const b = methodA(); b.c = "out-put bind"; return b; }
module.exports = {
methodB
}
my-file.test.js
const { methodA, methodB } = require('./my-file.js');
describe('methodB testing', () => {
it('should call methodA', () => {
methodB();
expect(methodA).toHaveBeenCalled()
}
});
here methodA is private method, so it is not explicit to the test file, then how i ensure it is called or not in the test files
There is no way to test the private function, only the alternative way found is to test the outputs like
const { methodA, methodB } = require('./my-file.js');
describe('methodB testing', () => {
it('should call methodA', () => {
const result = methodB();
expect(result.b.c).toBe("out-put bind")
}
});

clear encapsulated value in imported module - Jest js

Let's say I'm importing to Jest module like:
let var;
export const getVar = () => {
if(var == null) {
var = Date.now()
}
return var;
}
I am trying to make unit tests for this module, however, on every unit test I have to reset the "var" value. I've tried to redefine the module using require on "beforeEach" method but it does not work. Does anyone know how to reset encapsulated values like this?
Using old require syntax and resetModules
foo.js
let foo;
const getFoo = () => {
if (!foo) {
foo = "foo";
} else {
foo = "bar";
}
return foo;
};
module.exports = { getFoo };
foo.test.js
beforeEach(() => jest.resetModules());
test("first", () => {
const { getFoo } = require("./foo");
expect(getFoo()).toBe("foo");
});
test("second", () => {
const { getFoo } = require("./foo");
expect(getFoo()).toBe("foo");
});
Using dynamic import and resetModules
foo.js
let foo;
export const getFoo = () => {
if (!foo) {
foo = "foo";
} else {
foo = "bar";
}
return foo;
};
foo.test.js
import { jest } from "#jest/globals";
beforeEach(async () => {
jest.resetModules();
});
test("first", async () => {
const { getFoo } = await import("./foo.js");
expect(getFoo()).toBe("foo");
});
test("second", async () => {
const { getFoo } = await import("./foo.js");
expect(getFoo()).toBe("foo");
});

How to undo mocked require in jest?

I am mocking a library by doing this:
let helperFn;
let mock;
beforeEach(() => {
mock = jest.fn();
require('./helperFn').default = mock;
})
If I do this in a test, does it mean that from now on within the whole test suite that default function of helperFn will be associated with that mock?
In the Jest documentations I see how to reset the mock, but I don't see how to remove the mock from a required function. I am concerned that from that test on, all the calls into helperFn.default will see that mock.
ES6 modules
Here is an ES6 example:
helperFn.js
export default () => 'original';
code.js
import helperFn from './helperFn';
export const func = () => helperFn();
code.test.js
import * as helperFnModule from './helperFn';
import { func } from './code';
describe('helperFn mocked', () => {
let mock;
beforeEach(() => {
mock = jest.spyOn(helperFnModule, 'default');
mock.mockReturnValue('mocked');
});
afterEach(() => {
mock.mockRestore();
});
test('func', () => {
expect(func()).toBe('mocked'); // Success!
});
});
describe('helperFn not mocked', () => {
test('func', () => {
expect(func()).toBe('original'); // Success!
});
});
Details
Since ES6 imports are live views of the module exports, it is easy to mock an export and then restore it afterwards.
Node.js modules
Here is a Node.js example:
helperFn.js
exports.default = () => 'original';
code.js
const helperFn = require('./helperFn').default;
exports.func = () => helperFn();
code.test.js
describe('helperFn mocked', () => {
beforeEach(() => {
const helperFnModule = require('./helperFn');
helperFnModule.default = jest.fn(() => 'mocked');
});
afterEach(() => {
jest.resetModules();
});
test('func', () => {
const { func } = require('./code');
expect(func()).toBe('mocked'); // Success!
});
});
describe('helperFn not mocked', () => {
test('func', () => {
const { func } = require('./code');
expect(func()).toBe('original'); // Success!
});
});
Details
The default export gets remembered by code.js when it runs, so changing the default export of helperFn.js doesn't affect func once code.js is required. Jest also caches modules and returns the same module for multiple require calls unless jest.resetModules is called.
So for Node.js modules it is often easiest to require code within the test itself and use jest.resetModules to reset any mocking.

Custom webpack resolver plugin change #meow to ./meow.js

I am trying to create a webpack resolver that will convert the import for #meow to the import of ./meow.js. I have some basic code below that shows main imports #meow and the resolver should be converting all require statements to be ./meow.js.
meow.js
module.export = 'meow';
main.js
import meow from '#meow';
console.log(meow);
Resolver.js
module.exports = class Resolver {
apply(compiler) {
compiler.hooks.module.tapPromise('Resolver', async (init, resolveContext) => {
return compiler.doResolve(compiler.hooks.module, init, './meow.js', resolveContext, () => {});
});
}
}
Here's an example I got working with webpack 4.
class Example {
constructor() {
this.name = 'Example';
}
apply(compiler) {
compiler.hooks.resolve.tapPromise(this.name, async (init, context) => {
const callback = () => {};
if (init.request.match('#')) {
init.request = './meow.js'
return compiler.doResolve(compiler.hooks.resolve, init, null, context, callback)
} else {
return callback()
}
})
}
}
module.exports = Example;
I think that it is better to utilize NormalModuleReplacementPlugin to replace all the imports that are matched to some rule.
module.exports = function(env) {
return {
plugins: [
new webpack.NormalModuleReplacementPlugin(/(.*)#meow(\.*)/, function(resource) {
// do what ever mapping
resource.request = resource.request.replace(/#meow/, `meow.js`);
})
]
};
};

sinon mock not catching calls

I am having a hard time understanding what I am doing wrong.
I have a JS class as such:
export default class A {
constructor(repository) {
this._repository = repository;
}
async process(date) {
// ...
this._repository.writeToTable(entry);
}
}
and I am attempting to write a test that mocks the repository using sinon.mock
This is what I have so far:
describe('A', () => {
describe('#process(date)', () => {
it('should work', async () => {
const repository = { writeToTable: () => {} };
const mock = sinon.mock(repository);
const a = new A(repository);
await a.process('2017-06-16');
mock.expects('writeToTable').once();
mock.verify();
});
});
});
but it always fails saying that
ExpectationError: Expected writeToTable([...]) once (never called)
I've checked (added a console.log) and it is calling the object I defined on the test.
I ran this locally and read the documentation on sinonjs.org and you seem to be doing everything right.
I tried re-writing your example using a spy and ended up with something like this to get a passing test:
import sinon from "sinon";
import { expect } from "chai";
import A from "./index.js";
describe("A", () => {
describe("#process(date)", () => {
it("should work", async () => {
const repository = { writeToTable: sinon.spy() };
const a = new A(repository);
await a.process("2017-06-16");
expect(repository.writeToTable.calledOnce).to.be.true;
});
});
});

Categories