How to create re-usable mocks in mocha - javascript

Say I want to create use sinon.stub to mock an object:
beforeEach(() => {
this.someMethod = stub(SomeObject, 'someMethod');
});
afterEach(() => {
this.someMethod.restore();
});
How can I move this code into a re-usable mock file so that I can include it in every test where the SomeObject needs to be mocked?

I ended up creating hooks to pass into my functions:
// SomeObjectMock
export function beforeEachHook(SomeObject) {
return function() {
this.someMethod = stub(SomeObject, 'someMethod');
}.bind(this);
}
export function afterEachHook() {
return function() {
this.someMethod.restore();
}.bind(this);
}
Then in my test file:
import {afterEachHook, beforeEachHook} from './SomeObjectMock';
describe('myTest', () => {
beforeEach(beforeEachHook.bind(this)(SomeObject));
afterEach(afterEachHook.bind(this)());
});

Related

How to spyOn an exported standalone function using javascript jest?

It is a very simple scenario but I've struggled to find an answer for it.
helpers.ts:
export function foo() {
bar();
}
export function bar() {
// do something
}
helpers.spec.ts:
import { foo, bar } from "./helpers";
describe("tests", () => {
it("example test", () => {
const barSpy = // how can i set this up?
foo();
expect(barSpy).toHaveBeenCalled();
});
});
I can't do const spy = jest.spyOn(baz, 'bar'); because I don't have a module/class to put in place of "baz". It is just an exported function.
Edit:
Jest mock inner function has been suggested as a duplicate but unfortunately it doesn't help with my scenario.
Solutions in that question:
Move to separate module: I cannot do this for my scenario. If I am testing every function in my application, this would result in me creating 10s of new files which is not ideal. (To clarify, I think this solution would work but I cannot use it for my scenario. I am already mocking a separate file function successfully in this test file.)
Import the module into itself:
helpers.spec.ts:
import * as helpers from "./helpers";
describe("tests", () => {
it("example test", () => {
const barSpy = jest.spyOn(helpers, 'bar');
foo();
expect(barSpy).toHaveBeenCalled();
});
});
results in:
expect(jest.fn()).toHaveBeenCalled()
Expected number of calls: >= 1
Received number of calls: 0
This is the closed solution:
export function bar() {
// do something
}
export function foo() {
exports.bar(); // <-- have to change to exports.bar() instead of bar()
// or this.bar(); would also work.
}
import * as utils from './utils';
describe('tests', () => {
it('example test', () => {
const barSpy = jest.spyOn(utils, 'bar');
utils.foo();
expect(barSpy).toHaveBeenCalled();
});
});
Or take a look this duplicated question

Mock an import from another file but still return a mock value

I'm testing a function which calls another function imported from anotherFile. That outsideFunc returns an object which contains 'name'. I need this to exist in order to progress through the rest of my test/the function to work correctly.
systemUnderTest.js
import { outsideFunc } from './anotherFile.js';
function myFunc() {
const name = outsideFunc().name;
}
anotherFile.js:
export function outsideFunc() {
return { name : bob }
}
I don't care about testing anotherFile or the result of outsideFunc, but I still need to return a mock value as part of testing myFunc;
systemUnderTest.spec.js
describe("A situation", () => {
jest.mock("./anotherFile", () => ({
outsideFunc: jest.fn().mockReturnValue({
name: 'alice'
})
}));
it("Should continue through the function steps with no problems", () => {
expect(excludeCurrentProduct(initialState)).toBe('whatever Im testing');
});
});
The problem I get is that, when the unit test is working through myFunc, const name returns undefined where it should return alice. I would expect it to get the data from my jest.mock of the anotherFile file and its mock exported function, but it doesn't get the right response.
When I asset that I expect name = alice I actually get name = undefined.
systemUnderTest.js
import { outsideFunc } from './anotherFile.js';
// let's say that the function is exported
export function myFunc() {
const name = outsideFunc().name;
// and let's say that the function returns the name
return name;
}
you can describe in your
systemUnderTest.spec.js
import { myFunc } from './systemUnderTest';
import { outsideFunc } from './anotherFile';
// using auto-mocking has multiple advantages
// for example if the outsideFunc is deleted the test will fail
jest.mock('./anotherFile');
describe('myFunc', () => {
describe('if outsideFunc returns lemons', () => {
outsideFunc.mockReturnValue({name: 'lemons'});
it('should return lemons as well', () => {
expect(myFunc()).toEqual('lemons');
});
});
});
working example

Spy on a method of an object created dynamically during test execution

I need to spyOn a method of an object created dynamically inside another method
So consider the following:
public doStuff = () => {
const myThing = new MyThing();
myThing.doSomethingElse().then((data) => {
//do more stuff here...
})
}
I want to therefore spyOn the instance of MyThing and the call to doSomethingElse().
I did come across a solution here which make use of the object's prototype, which I attempted like so
spyOn(MyThing.prototype, 'doSomethingElse').and.returnValue(Promise.resolve({foo: 'bar'}));
But this does not work after I call doStuff() in my tests, I get an error:
Error: : doSomethingElse() method does not exist
But I know this method is fine, since it runs as expected locally.
I am unsure how to proceed, can anyone assist?
Thanks
You might be missing something in your test. This is a simple test and you can see it works as you expected
require("jasmine");
class MyThing {
async doSomethingElse() {
return { bar: "foo" };
}
}
class Stuff {
doStuff() {
const myThing = new MyThing();
myThing.doSomethingElse().then(data => {
console.log(data);
});
}
}
describe("doSomethingElse", () => {
it("toHaveBeenCalled", () => {
spyOn(MyThing.prototype, "doSomethingElse").and.returnValue(
Promise.resolve({ foo: "bar123" })
);
const stuff = new Stuff();
stuff.doStuff();
expect(MyThing.prototype.doSomethingElse).toHaveBeenCalled();
});
});
The key is to use the spyOn before you instantiate the class that contains the doStuff function. If we move the const stuff = new Stuff(); above the spyOn it fails.
Hope it helps
You can create a spy object with mock method by passing in an object where the property names represent returned data for methods.
describe('test', () => {
let mock;
beforeEach(() => {
mock = jasmine.createSpyObj('mock', {
doSomethingElse: Promise.resolve({foo: 'bar'})
});
});
it('call mock', async () => {
const result = await mock.doSomethingElse();
expect(result.foo).toEqual('bar');
});
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.4.0/jasmine.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.4.0/jasmine.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.4.0/jasmine-html.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.4.0/boot.js"></script>

How to test class instance inside a function with Jest

I have the following hypothetical scenario:
// file MyClass.js in an external package
class MyClass {
myfunc = () => {
// do something
}
}
// file in my project
function myFunctionToBeTested() {
const instance = new MyClass()
instance.myFunc()
}
I need to create a test with Jest that makes sure instance.myFunc was called
One of the option is to replace MyClass module with mock implementation
const mockmyfunc = jest.fn()
jest.mock("path/to/external/package/MyClass", () => {
return jest.fn().mockImplementation(() => {
return {myfunc: mockmyfunc}
})
})
And then write following test
it("Test myfunc called in functionToBeTested", () => {
functionToBeTested()
expect(mockmyfunc).toHaveBeenCalled()
})
Note that this is not the only way, you can dive into https://facebook.github.io/jest/docs/en/es6-class-mocks.html for other alternatives.
Update
If the myfunc would be an actual function (which i guess is not an option since it's external package?)
export class MyClass {
myFunc() {
// do smth
}
}
and you would not need to replace the implementation, you could be using jest's automock
import MyClass from "path/to/external/package/MyClass"
jest.mock("path/to/external/package/MyClass")
it("Test myfunc called in functionToBeTested", () => {
functionToBeTested()
const mockMyFunc = MyClass.mock.instances[0].myFunc
expect(mockMyFunc).toHaveBeenCalled()
})
you can mock out the class and assign the default export of that file to a variable as follows:
jest.mock('../../utils/api/api');
const FakeClass = require('../someFile.js').default;
then access calls to a function on your mock class like this:
FakeClass.prototype.myFunc.mock.calls

How to test if function was called with defined parameters ( toHaveBeenCalledWith ) with Jest

I want to test, if particular function was called in my test and with the correct parameters. From JEST documentation I'm not able to figure out, what is the correct way to do it.
Let's say I have something like this:
// add.js
function child(ch) {
const t = ch + 1;
// no return value here. Function has some other "side effect"
}
function main(a) {
if (a == 2) {
child(a + 2);
}
return a + 1;
}
exports.main = main;
exports.child = child;
Now in unit test:
1.
I want to run main(1) and test that it returned 2 and child() was not called.
2.
And then I want to run main(2) and thest that it returned 3 and child(4) was called exactly once.
I have something like this now:
// add-spec.js
module = require('./add');
describe('main', () => {
it('should add one and not call child Fn', () => {
expect(module.main(1)).toBe(2);
// TODO: child() was not called
});
it('should add one andcall child Fn', () => {
expect(module.main(2)).toBe(3);
// TODO: child() was called with param 4 exactly once
// expect(module.child).toHaveBeenCalledWith(4);
});
});
I'm testing this in https://repl.it/languages/jest , so a working example in this REPL will be much appreciated.
OK, I've figured it out. The trick is, to split functions into separate files. So the code is (and works in https://repl.it/languages/jest ):
// add.js
child = require('./child').child;
function main(a) {
if (a == 2) {
child(a + 2);
}
return a + 1;
}
exports.main = main;
extracted child.js file:
// child.js
function child(ch) {
const t = ch + 1;
// no return value here. Function has some other "side effect"
}
exports.child = child;
main test file:
// add-spec.js
main = require('./add').main;
child = require('./child').child;
child = jest.fn();
describe('main', () => {
it('should add one and not call child Fn', () => {
expect(main(1)).toBe(2);
expect(child).toHaveBeenCalledTimes(0);
});
it('should add one andcall child Fn', () => {
expect(main(2)).toBe(3);
expect(child).toHaveBeenCalledWith(4);
expect(child).toHaveBeenCalledTimes(1);
});
});
let child = require('./child');
let main = require('./add').main;
// name of the module, name of the function
spy = jest.spyOn(child, 'child');
describe('main', () => {
it('should add one and call child Fn', () => {
expect(main(1)).toBe(2);
// Called or not
expect(spy).toHaveBeenCalled();
});
});
In my case I had a similar doubt with angular code so I have a method, that is invoked when a field in a form is changed, and the only task of this method is to trigger some other methods.
Code extract:
handleConnectionToLegChange(value) {
if (!isNullOrUndefined(value)) {
this.connectionsForm.controls.to.markAsDirty();
this.connectionsForm.controls.to.updateValueAndValidity();
this.connectionsForm.controls.from.markAsDirty();
this.connectionsForm.controls.from.updateValueAndValidity();
this.updateModalButtonStatus(this.connectionsSubject);
}}
So in order to test it I used this test case. (I just spied on 2 of the 5 triggered methods but that's enough on my case.)
test extract:
it('should execute fn handleConnectionToLegChange and check method calls if value is not null', () => {
component.connectionsForm.controls.to.updateValueAndValidity = jest.fn();
component.updateModalButtonStatus = jest.fn();
component.handleConnectionToLegChange('a');
expect(component.connectionsForm.controls.to.updateValueAndValidity).toHaveBeenCalled();
expect(component.updateModalButtonStatus).toHaveBeenCalled(); });
It worked fine for me.
To be mocked:
// child.js
function child(ch) {
console.log('some side effects happen in here', ch);
}
exports.child = child;
To be tested:
// main.js
const { child } = require('./child');
function main(a) {
if (a == 2) {
child(a + 2);
}
return a + 1;
}
exports.main = main;
Test for main.js
// main.test.js
jest.mock('./child');
const { main } = require('./main');
// This is the mocked version of "child"
const { child } = require('./child');
describe('main', () => {
it('should add one and not call child Fn', () => {
expect(main(1)).toBe(2);
expect(child).toHaveBeenCalledTimes(0);
});
it('should add one and call child Fn', () => {
expect(main(2)).toBe(3);
expect(child).toHaveBeenCalledWith(4);
expect(child).toHaveBeenCalledTimes(1);
});
});

Categories

Resources