Stubbing function with jest - javascript

I understand that this is the basic function of jest and I should be able to figure it out with the docs and other online resources, but somehow I cannot, so I apologise in advance if this is trivial.
I'm trying to test a function in Javascript that performs a few operations using other modules, as well as localStorage, and I would like to stub out the other modules and the call to localStorage. The documentation that I found for Jest seemed far too simplistic for me to adapt to my use case, like declaring a mock and then calling it inside the test - this doesn't happen in my case, as the function I want to mock is being called internally by my function, I'm not passing it in as a dependency. Let me give some code to explain: file name is dataInLocalStorage.js
import serialize from './serialize'; // simple module that serialises data
import deserialize from './deserialize'; // simple module that deserialises data
import findObject from './findObject'; // find an object in the deserialised data
const addDataToLocalStorage = (data) => {
const dataStored = deserialize(localStorage.getItem('data')); // fetch data from localStorage
const isStored = !!findObject(dataStored, data); // check whether the data I want to store is already there
if (isStored) { return null; } // if data is already stored, skip
const serializedData = serialize(data); // serialise data to be stored
return localStorage.setItem('data', serializedData); // store serialised data in localStorage
};
export { addDataToLocalStorage };
The purpose os this module is just to store data in localStorage in a serialised way, but in an additive way, so that adding data doesn't remove previously stored data, and no duplicates are added either.
Anyway, my test file looks like this: file name is dataInLocalStorage.test.js
import { addDataToLocalStorage } from '../dataInLocalStorage';
describe('addDataToLocalStorage', () => {
const deserialize = jest.fn();
beforeAll(() => {
localStorage.removeItem('data');
});
const data = {
name: 'johnny'
};
addDataToLocalStorage(data);
it('adds the data to local storage', () => {
expect(deserialize).toHaveBeenCalled();
});
});
Here is the rather unsurprising error for this attempt.
expect(jest.fn()).toHaveBeenCalled()
Expected mock function to have been called, but it was not called.
17 |
18 | it('adds the data to local storage', () => {
> 19 | expect(deserialize).toHaveBeenCalled();
| ^
20 | });
21 | });
On top of this I tried importing the deserialize function here in the test file and adding a jest.mock on that, which didn't work either.
Note that this isn't my code 100%, I have modified it for simplicity in order to make it easier to read for you, sorry if there are some slight mismatches, I tried my best to be as diligent as possible while converting it.
If you know what you're looking at, you'll see that this is obviously not working. Using other (more useful) expectations, the test was passing, but adding some console logs in the deserialize file showed that it's still running, when the idea is that I would like to mock it and provide my own return value.
Side note: I came from Ruby on Rails where mocking with RSpec is pretty simple, and I was hoping it would be just as simple with Jest. It likely is, but I can't wrap my head around it, as it doesn't seem possible to make a direct reference to the function/module I want to mock. In RSpec, doing allow(MySerializer).to receive(:call).and_return(...) would do the trick and I wouldn't have to worry about that module being called during the test.

When you set the value of deserialize to a jest mock, you are changing the variable value, not setting a reference that your code is using. To keep it a reference it needs to be a value in an object.
To import an object you can use import * as deserialize from "./deserialize";.
Then you can set the mock on the reference with deserialize.default = jest.fn().
https://codesandbox.io/s/88wlzp6q88
import { useIt } from "./use-default-export";
import * as myfunc from "./default-export-function";
test("use-default-export-function", () => {
expect(useIt()).toEqual("real");
});
test("use-default-export-function with mock", () => {
myfunc.default = jest.fn(() => "unreal");
expect(useIt()).toEqual("unreal");
});
in your test it'll be..
import { addDataToLocalStorage } from '../dataInLocalStorage';
import * as deserialize from './deserialize';
...
deserialize.default = jest.fn();
alternate TS compat version..
(which is actually cleaner all round..)
import { useIt } from "./use-default-export";
import myfunc from "./default-export-function";
jest.mock("./default-export-function", () => jest.fn());
test("use-default-export-function with mock", () => {
useIt();
expect(myfunc).toHaveBeenCalled();
});
return/resolve different values per test
(need to cast to jest.Mock to be able to use jest.fn() functions)
test("use-default-export-function with mock", () => {
const aFunc = myfunc as jest.Mock;
aFunc.mockResolvedValue("bar");
useIt();
expect(useIt()).resolves.toEqual("bar");
});
test("use-default-export-function with mock 2", () => {
const aFunc = myfunc as jest.Mock;
aFunc.mockReturnValue("foo");
useIt();
expect(useIt()).toEqual("foo");
});

Related

Cannot mock external node module

I am trying to mock an external module (jwt_decode for those interested), and I have seen many examples of how to mock external an node module using Jest both for all tests in a test suite, as well as on a per-test basis.
I have been able to mock the dependency so that it mocks the return value for all tests in the suite, although the default function is all that i'm really concerned with.
import jwt_decode from 'jwt-decode';
jest.mock('jwt-decode', () => jest.fn().mockReturnValue({
exp: 12345,
somethingElse: 'test_value'
}));
This works well, except I would like to test a scenario where the returned token has expired, so that I can verify that certain Redux actions were dispatched when this situation arises.
import jwt_decode from 'jwt-decode';
const decoded = jwt_decode(localStorage.jwtToken);
// set user info and isAuthenticated
store.dispatch(setCurrentUser(decoded));
// this is the scenario I am looking to test
const currentTime = Date.now() / 1000;
if (decoded.exp < currentTime) {
store.dispatch(logoutUser());
store.dispatch(clearCurrentProfile());
window.location.href = '/login';
}
I would like to modify the returned mock for an individual test so that I can ensure that the 'if' statement shown equates to false, and other parts of the code base are executed.
How can one go about this?
Some of the examples I have tried and failed with so far include:
test('some test that will use a different mock' () => {
// re-assign value of the imported module using the its given alias
jwt_decode = jest.fn().mockImplementation(() => {
return {
exp: 'something_different 9999999999',
somethingElse: 'I_changed_too'
};
});
});
As well as
jwt_decode.default = jest.fn().mockImplementation(() => {
return {
exp: 'something_different 9999999999',
somethingElse: 'I_changed_too'
};
});
And also jest.spyOn(), as seen in this SO question, as well as A Jar of Clay's answers on the same question, which proposes the following:
import { funcToMock } from './somewhere';
jest.mock('./somewhere');
beforeEach(() => {
funcToMock.mockImplementation(() => { /* default implementation */ });
});
test('case that needs a different implementation of funcToMock', () => {
funcToMock.mockImplementation(() => { /* implementation specific to this test */ });
// ...
});
I also found a suggestion for creating a util which changes the global localStorage on a test by test basis, but I would rather not use a real jsonwebtoken, or have to worry about storing sign-in credentials.
What I keep ending up with is that jwt_decode is not updated when running the test that should have the different mock value returned, or what's more common is that I get an error saying that ".default is not a function".
If you have suggestions, I would be very grateful.
Assuming I understand your ultimate goal, how about this approach:
In your project directory, at the same level as node_modules, create a directory called "__mocks__" and in that directory, put a file called "jwt-decode.js" -- with this in place there is no need to explicitly mock jwt-decode in your test module, as it will always be mocked automatically.
Put the following code in your __mocks__/jst_decode.js file:
export default token => token;
Thus, when the code that you're testing calls jwt_decode(something), the return value will be exactly what was passed in.
Now you can write unit tests that test the behavior of your module given various valid or invalid values in the token; just mock the token value in your module and your mocked implementation of jwt_decode() will simply pass it through.

How do I pass a dependency to a Vue component?

I have a class that is intended to serve as a repository for making AJAX requests using axios:
export default class SomeRepository {
getEncryptedValue (value) {
return axios.get('http://localhost/api/v1/encrypt')
}
}
And I have a component that calls this repository's method inside the methods property in the component, like so:
<template>
...
</template>
<script>
import SomeRepository from '#/classes/SomeRepository'
export default {
data () {
return {
value: '',
result: ''
}
},
methods: {
encrypt () {
let someRepo = new SomeRepository()
someRepo.getEncryptedValue(this.value)
.then(response => {
this.result = response.data.result
})
}
}
}
</script>
How can I pass SomeRepository as a dependency? My goal is that I want to mock it in my unit tests.
I am looking for a straightforward solution, preferably one that does not involve third-party libraries or boilerplate.
I've tried also doing:
import Vue from 'vue'
import SomeRepository from '#/classes/SomeRepository'
Vue.use(SomeRepository)
// and
Vue.use(new SomeRepository())
which clearly don't work (honestly, I didn't expect them to). I also tried following this article https://codeburst.io/dependency-injection-with-vue-js-f6b44a0dae6d but I did not really like the approach of having to create a mixin every time I want to use a dependency.
I've also considered passing it as a prop, but was not sure where to instantiate the object before passing it.
There is a ton of ways to mock functions. The most simplistic that I have found is simply accepting dependencies as parameters of a function and replace the function to use in your tests.
Example of Method
// Function
function func(dependency) {
dependency.use()
}
// In App
func(dependency) // -> runs dependency.use
// In Tests
func(mockDependency) // -> runs mocked dependency.use
Your Example
Once you know this method you can do a ton of different versions of it but to show a super simple version.
encrypt() {
this._encrypt(SomeRepository)
}
_encrypt (SomeRepository) {
let someRepo = new SomeRepository()
someRepo.getEncryptedValue(this.value)
.then(response => {
this.result = response.data.result
})
}
You would test _encrypt with the mock dependency.
You can also do something like this.
encrypt (_SomeRepository=SomeRepository) {
let someRepo = new _SomeRepository()
someRepo.getEncryptedValue(this.value)
.then(response => {
this.result = response.data.result
})
}
If you pass in the mocked version it will use that if you do not it will use the real one.
The idea is to use this method however you see fit, but I think you get the idea. It is super simple no magic and no libraries needed.
You should use a Vue plugin
https://v2.vuejs.org/v2/guide/plugins.html
Then you can access it via e.g. this.$someDeps
For unit testing, you can mock it easily using Vue Test Utils https://vue-test-utils.vuejs.org/api/options.html#mocks

Why does mutating a module update the reference if calling that module from another module, but not if calling from itself?

This question pertains to testing javascript and mocking functions.
Say I have a module that looks like this:
export function alpha(n) {
return `${n}${beta(n)}${n}`;
}
export function beta(n) {
return new Array(n).fill(0).map(() => ".").join("");
}
Then I can't test it the following way:
import * as indexModule from "./index";
//Not what we want to do, because we want to mock the functionality of beta
describe("alpha, large test", () => {
it("alpha(1) returns '1.1'", () => {
expect(indexModule.alpha(1)).toEqual("1.1"); //PASS
});
it("alpha(3) returns '3...3'", () => {
expect(indexModule.alpha(3)).toEqual("3...3"); //PASS
});
});
//Simple atomic test
describe("beta", () => {
it("beta(3) returns '...'", () => {
expect(indexModule.beta(3)).toEqual("..."); //FAIL: received: 'x'
});
});
//Here we are trying to mutate the beta function to mock its functionality
describe("alpha", () => {
indexModule.beta = (n) => "x";
it("works", () => {
expect(indexModule.alpha(3)).toEqual("3x3"); //FAIL, recieved: '3...3'
});
});
However, if split the module into two:
alpha.js
import { beta } from "./beta";
export function alpha(n) {
return `${n}${beta(n)}${n}`;
}
beta.js
export function beta(n) {
return new Array(n).fill(0).map(() => ".").join("");
}
Then I can mutate the beta module, and alpha knows about it:
import { alpha } from "./alpha";
import * as betaModule from "./beta";
describe("alpha", () => {
betaModule.beta = (n) => "x";
it("works", () => {
expect(alpha(3)).toEqual("3x3"); //PASS
});
});
Why is this the case? I'm looking for a technically specific answer.
I have a Github branch with this code here, see the mutateModule and singleFunctionPerModuleAndMutate folders.
As an additional question - in this example I am mutating the module by directly reassigning properties. Am I right in understanding that using jest mock functionality is going to be essentially doing the same thing?
ie. If the reason that the first example doesn't work but the second doesn't is due to the mutation, then it necceserily means that using the jest module mocking functions is similarly not going to work.
As far as I know - there is not way to mock a single function in a module, while testing that module, as this jest github issues talks about. What I'm wanting to know - is why this is.
Why does mutating a module update the reference if calling that module from another module, but not if calling from itself?
"In ES6, imports are live read-only views on exported values".
When you import an ES6 module you essentially get a live view of what is exported by that module.
The live view can be mutated, and any code that imports a live view of the module exports will see the mutation.
That is why your test works when alpha and beta are in two different modules. The test modifies the live view of the beta module, and since the alpha module uses the live view of the beta module, it automatically uses the mocked function instead of the original.
On the other hand, in the code above alpha and beta are in the same module and alpha calls beta directly. alpha does not use the live view of the module so when the test modifies the live view of the module it has no effect.
As an additional question - in this example I am mutating the module by directly reassigning properties. Am I right in understanding that using jest mock functionality is going to be essentially doing the same thing?
There are a few ways to mock things using Jest.
One of the ways is using jest.spyOn which accepts an object and a method name and replaces the method on the object with a spy that calls the original method.
A common way to use jest.spyOn is to pass it the live view of an ES6 module as the object which mutates the live view of the module.
So yes, mocking by passing the live view of an ES6 module to something like jest.spyOn (or spyOn from Jasmine, or sinon.spy from Sinon, etc.) mutates the live view of the module in essentially the same way as directly mutating the live view of the module like you are doing in the code above.
As far as I know - there is not way to mock a single function in a module, while testing that module, as this jest github issues talks about. What I'm wanting to know - is why this is.
Actually, it is possible.
"ES6 modules support cyclic dependencies automatically" which means that the live view of a module can be imported into the module itself.
As long as alpha calls beta using the live view of the module that beta is defined in, then beta can be mocked during the test. This works even if they are defined in the same module:
import * as indexModule from './index' // import the live view of the module
export function alpha(n) {
return `${n}${indexModule.beta(n)}${n}`; // call beta using the live view of the module
}
export function beta(n) {
return new Array(n).fill(0).map(() => ".").join("");
}
What I find interesting is that none of your code would work in a browser.
Module ("./some/path/to/file.js"):
const x = () => "x"
const y = () => "y"
export { x, y }
You cannot modify a named import as they are constants:
import { x } from "./some/path/to/file.js"
x = () => {} //Assignment to constant variable.
You also cannot assign to a readonly property of a namespace import.
import * as stuff from "./some/path/to/file.js"
stuff.y = () => {} //Cannot assign to read only property 'y' of...
Here's a codepen which also shows why indexModule.alpha !== alpha from the module: https://codepen.io/bluewater86/pen/QYwMPa
You are using the module to encapsulate your two functions but for the reasons above, this is a bad idea. You really need to encapsulate those functions in a class so that you can mock them appropriately.
//alphaBeta.js
export const beta = n => new Array(n).fill(0).map(() => ".").join("");
export default class alphaBeta {
static get beta() { return beta }
beta(n) {
beta(n)
}
alpha(n) {
return `${n}${this.beta(n)}${n}`;
}
}
export { alphaBeta }
And finally by moving to default/named imports instead of namespace imports you will have no need to use the cyclic dependency hack. Using default/named imports means that you will be importing the same in-memory view of the exports that the module exported. i.e. importer.beta === exporter.beta
import alphaBetaDefault, { alphaBeta, beta } from "./alphaBeta.js"
alphaBeta.prototype.beta = (n) => "x";
describe("alphaBeta", () => {
it("Imported function === exported function", () => {
expect(alphaBeta.beta).toEqual(beta); //PASS
});
const alphaBetaObject = new alphaBeta
it("Has been mocked", () => {
expect(alphaBetaObject.alpha(3)).toEqual("3x3");
});
alphaBeta.prototype.beta = (n) => "z";
it("Is still connected to its prototype", () => {
expect(alphaBetaObject.alpha(3)).toEqual("3z3");
});
const secondObject = new alphaBetaDefault
it("Will still be mocked for all imports of that module", () => {
expect(secondObject.alpha(3)).toEqual("3z3");
});
});

Check if functions have been called in unit test

Hi I'm trying to write some unit tests in Jest for a module I write, but kind of stuck currently and need some advice how to continue.
export const submitOrder = async (body, key) => {
const clientRepo = new ClientRepository(db)
const companyRepo = new CompanyRepository(db)
const company = await getCompanyByKey(
companyRepo,
key
);
const client = await createClient(
clientRepo,
body
);
await addClientToCompany(
companyRepo,
client.id,
company.id
);
.. More things
}
I can easily test each function(getCompanyByKey, createClient & addClientToCompany) by passing down a mocked repository.
But I would also like to test my "flow" of the submitOrder function, by checking if my repository functions have been called. But I would then need the instance of each repository, which I don't instantiate until my submitOrder function.
Something like this, which is similar how I unit test my functions.
jest.mock('../repositories/ClientRepository');
jest.mock('../repositories/CompanyRepository');
test('should be able to submit an order', async () => {
const apiKey = 'mocked-super-key';
const body = getMockData();
const result = await submitOrder(body, apiKey);
expect(result).toMatchSnapshot();
expect(CompanyRepository.findByKey).toHaveBeenCalled();
expect(ClientRepository.create).toHaveBeenCalled();
expect(CompanyRepository.addClient).toHaveBeenCalled();
});
Do you have any tips of how I can test if my repositories have been called?
The problem you describe is one of the motivating factors behind dependency injection.
As a single example: your submitOrder() code uses new to directly instantiate a client repository of the specific implementation ClientRepository. Instead, it could declare that it has a dependency - it needs an object that implements the interface of a client repository. It could then allow for such an object to be supplied by the surrounding environment (a "dependency injection container" in buzzword-ese). Then during testing you would create and provide ("inject") a mock implementation instead of the real implementation.
This has the added benefit that if you ever have to be able to select between multiple "real" implementations, you're already set up to do that too.
There are many ways to achieve this. It can be as simple as a design pattern, or for a more complete solution you could use a dependency injection framework.
If you absolutely cannot refactor your code for this practice, then JavaScript is dynamic enough that you can probably cobble together a way to intercept the invocation of new and thereby simulate dependency injection.
You can pass a mock implementation factory as a second parameter to jest.mock, as described in the docs.
You can use this to mock out the methods that you want to check to have been called.
Try this:
jest.mock('../repositories/CompanyRepository', () => {
findByKey: jest.fn(),
addClient: jest.jn()
});
const mockCreate = jest.fn();
jest.mock('../repositories/CompanyRepository', () => class {
create(...args) {
mockCreate(...args);
}
});
test('should be able to submit an order', async () => {
const apiKey = 'mocked-super-key';
const body = getMockData();
const result = await submitOrder(body, apiKey);
expect(result).toMatchSnapshot();
expect(CompanyRepository.findByKey).toHaveBeenCalled();
expect(ClientRepository.create).toHaveBeenCalled();
expect(CompanyRepository.addClient).toHaveBeenCalled();
});
Since CompanyRepository is created with “new”, we use a class definition in this case and pass in a mock function that is called when the “create” method is invoked.

EmberJS: Unit test class-based helpers

I have several class-based helpers in my app, mostly so I can include the i18n service. Everything works nicely, however, I can't figure out a way to test them.
The auto generated test is not working, as it expects a function to be exported and complains, that undefined is not a constructor:
module('Unit | Helper | format number');
// Replace this with your real tests.
test('it works', function(assert) {
let result = formatNumber([42]);
assert.ok(result);
});
So I tried using moduleFor as it already worked for testing mixins, but also failed:
moduleFor('helper:format-number', 'Unit | Helper | format number', {
needs: ['service:i18n']
});
test('it works', function(assert) {
let result = FormatNumber.compute(42);
assert.ok(result);
});
I tried all the different versions of instantiating the helper object and calling compute on it but nothing worked. In the end it either always returned null or failed with an undefined error.
Did anyone manage to succeed where I failed?
The issue in your example is that you're trying to call compute as a static method on the class itself, when what you really need is an instance of the class.
Here's an example for a Class-based helper that I'm testing. Note; it uses mocha, not qunit, but all of the concepts are the same.
import { expect } from 'chai';
import { beforeEach, describe, it } from 'mocha';
import ShareImage from 'my-app/helpers/share-image';
describe('ShareImageHelper', function() {
beforeEach(function() {
this.helperClass = ShareImage.create({
location: {
hostWithProtocolAndPort: ''
}
});
this.helper = this.helperClass.compute.bind(this.helperClass);
});
it('calculates the `assetPath` correctly', function() {
const assetPath = this.helperClass.get('assetPath');
expect(assetPath).to.equal('/assets/images/social/');
});
it('calculates the path to an image correctly', function() {
const value = this.helper(['foo', 'bar', 'baz']);
expect(value).to.equal('/assets/images/social/foo/bar/baz.png');
});
});
The key here is that on each test run (in the beforeEach callback) I create a new instance of the helper, and then create a function that my tests can call the same way the template helper will be called (this.helper). This allows the tests to look as much like the real code as possible, while still giving me the ability to modify the helper class during tests, which you can also see in the beforeEach callback.

Categories

Resources