Calling 'created' method inside Jest tests on Vue.js - javascript

I'm testing my Activity.vue component on Vue.js with Jest.
My Activity.vue component calls the initializeTable() method inside created() in order to initialize my tableRows property.
The problem is, when I run my tests, Jest does not call 'created()' method and, for that reason, it does not initialize my tableRows property, then the test says that my tableRows property is equal to [] (original state).
How could I call created() method when I'm testing with Jest?
I've already tried to call it explicitly inside the test (i.e. wrapper.vm.$created()), but to no avail.
Would anyone know how to solve it?
I give my code below.
Thank you in advance.
Activity.spec.js
import { shallowMount } from '#vue/test-utils'
import Activity from '#/views/Activity.vue'
import api from '#/assets/js/api'
const expectedTableRows = [...]
const $t = () => {}
api.getActivity = jest.fn(
() => Promise.resolve({
data: [...]
})
)
describe('Activity.vue', () => {
it('renders vm.tableRows in created()', () => {
const wrapper = shallowMount(Activity, {
mocks: { $t }
})
// wrapper.vm.$created()
expect(wrapper.vm.tableRows).toEqual(expectedTableRows)
})
})
Activity.vue
import api from '#/assets/js/api'
export default {
data () {
return {
tableRows: []
}
},
created () {
this.initializeTable()
},
methods: {
initializeTable () {
this.loading = true
api.getActivity().then(response => {
this.tableRows = response.data
}).catch(error => {
this.errorBackend = error
}).finally(() => {
this.loading = false
})
}
}
}
EDIT 1: Solution
As Husam Ibrahim described, the function is asynchronous, because of that, I needed to refactor the test using the following tips and now I was able to fix it.
I give the correct code below:
import flushPromises from 'flush-promises' // <--
...
describe('Activity.vue', () => {
it('renders wm.tableRows after created()', async () => { // <--
const wrapper = shallowMount(Activity, {
mocks: { $t }
})
await flushPromises() // <--
expect(wrapper.vm.tableRows).toEqual(expectedTableRows)
})
})

Related

get new Instance for function each test

I have some tests in one file,
I check my reducer with some case
My code looks like this
my code
import axiosInstance from '~/utils/network';
const fetcher = axiosInstance();
const fetchMiddleware = () => {
switch (type) {
case 'LOGOUT':{
try {
await fetcher.get(API.GET.LOGOUT_OPERATION);
dispatch({ type: 'LOGOUT_SUCCESS' });
} catch (err) {
dispatch({ type: 'LOGOUT_FAIL' });
}
});
}
}
}
my test
import axiosInstance from '../../src/utils/network';
import configureStore from 'redux-mock-store';
const middlewares = [fetchMiddleware, thunk];
const mockStore = configureStore(middlewares);
const store = mockStore(getInitialReducerState());
jest.mock('../../src/utils/network', () => {
const axiosInstance = jest.fn().mockImplementation(() => {
return {
get: jest.fn().mockImplementation(() => {
return {
headers: {},
};
}),
};
}) as any;
axiosInstance.configure = jest.fn();
return axiosInstance;
});
describe('test LOGOUT', () => {
beforeEach(() => {
store.clearActions();
});
it('should test be success', async () => {
await store.dispatch({
type: 'LOGOUT',
payload: { userName: 'testUserName' },
});
expect(store.getActions()).toContainEqual({
type: 'LOGOUT_SUCCESS',
});
});
it('should test be fail', async () => {
(axiosInstance as jest.Mock).mockImplementation(() => {
return {
get: jest.fn().mockImplementation(() => {
throw new Error(' ');
}),
};
});
await store.dispatch({
type: 'LOGOUT',
payload: { userName: 'testUserName' },
});
expect(store.getActions()).toContainEqual({
type: 'LOGOUT_FAIL',
});
});
});
I want to test two scenarios: success & fail,
I mock the axiosInstance function.
But even I override the mock in the second test I get the first mock because my code loads axiosInstance only once.
what can I do?
You need to use jest.isolateModules
Let's say we have 2 files:
./lib.js - this is your ~/utils/network
./repro.js - this is your file with the code under test
./lib.js:
export default function lib() {
return () => 10;
}
./repro.js:
import lib from './lib';
const fnInstance = lib();
export const fn = () => {
return fnInstance();
};
And the ./repro.test.js:
function getRepro(libMock) {
let repro;
// Must use isolateModules because we need to require a new module everytime
jest.isolateModules(() => {
jest.mock('./lib', () => {
return {
default: libMock,
};
});
repro = require('./repro');
});
// If for some reason in the future the behavior will change and this assertion will fail
// We can do a workaround by returning a Promise and the `resolve` callback will be called with the Component in the `isolateModules` function
// Or we can also put the whole test function inside the `isolateModules` (less preferred)
expect(repro).toBeDefined();
return repro;
}
describe('', () => {
it('should return 1', () => {
const { fn } = getRepro(function lib() {
return () => 1
});
expect(fn()).toEqual(1);
});
it('should return 2', () => {
const { fn } = getRepro(function lib() {
return () => 2
});
expect(fn()).toEqual(2);
});
});
It's preferable to use existing library to mock Axios, it saves from boilerplate code and potential mistakes in mock implementation; moxios has been already suggested.
It's inconvenient to mock axiosInstance per test because it has been already called on the import of tested module, so this requires it to be re-imported per test; another answer explains how it's done with jest.isolateModules.
Since axiosInstance is evaluated only once and is supposed to return mocked object, it's convenient to mock it once per test and then change implementations:
jest.mock('~/utils/network', () => {
const axiosMock = { get: jest.fn(), ... };
return {
axiosInstance: () => axiosMock;
};
});
const axiosMock = axiosInstance();
...
(axiosMock.get axiosInstance as jest.Mock).mockImplementation(() => {
throw new Error(' ');
});
await store.dispatch(...);
This requires to use jest.restoreAllMocks in beforeEach or similar Jest configuration option to avoid test cross-contamination.
Notice that Axios doesn't throw errors but rather return rejected promises, this may affect test results, see the note regarding the benefits of libraries.

When running a Jest test, a variable outside the function is undefined

This is my dev code below.
import { proxyManager } from '../../proxy'
const proxy = proxyManager.get()
// proxy is expected to be an object like this:
// { post: () => {}, get: () => {} }
export const updateNamePrefixToAll = ({ origPrefix, prefix, primDBId }) => {
return proxy?.post(
`/model/tables/physicalTables/tablePrefixes/batch?primaryDataSourceId=${primDBId}&oldTablePrefix=${encodeURL(
origPrefix
)}&newTablePrefix=${encodeURL(prefix)}`
)
}
The test code is shown below
import {
updateNamePrefixToAll,
} from '#tableList/service'
describe('service.ts', () => {
it('', () => {
updateNamePrefixToAll({
origPrefix: 'origPrefix1',
prefix: 'prefix1',
primDBId: 'primDBId1',
})
})
})
After running the test, there is an error.
ReferenceError: proxy is not defined
I have solved this error by mocking the module below
import { proxyManager } from '../../proxy'
Beside this, how can I solve this issue?

How do you mock a named exports constructor and functions of an external library with Jest?

I have seen similar questions but nothing I have seen in the documentation or stackoverflow describes what I am trying to do. I am new to javascript and just started using jest, I have read through the jest documentation but I have not seen an example that mocks a named export of an external library. The library I am trying to mock is rate-limiter-flexible. I want to mock the named export RateLimiterRedis. I need to mock a couple of RateLimiterRedis functions, including get, consume, and delete.
For example when I mocked a function from redis all I had to do was:
import redis from 'redis';
jest.mock('redis', () => {
return { createClient: jest.fn()};
});
When I try:
jest.mock('rate-limiter-flexible', () => {
return jest.fn().mockImplementation(() => {
return { RateLimiterRedis: { get: mockGet } }
});
});
I get: TypeError: _rateLimiterFlexible.RateLimiterRedis is not a constructor
When I try:
jest.mock('rate-limiter-flexible', () => {
return { RateLimiterRedis: () => {}}
});
I get: TypeError: limiter.get is not a function
So it recognizes the constructor but I need to add the functions.
I have tried:
jest.mock('rate-limiter-flexible', () => {
return { RateLimiterRedis: () => {
return jest.fn().mockImplementation(() => {
return {
get: mockGet
}
})
},
}
});
This also gives: TypeError: limiter.get is not a function
This is in my file I am trying to test:
const limiter = new RateLimiterRedis(opts);
I have also tried doMocking the named export itself (since mock hoists itself to the top) to no success
My question boils down to how can I mock a constructor of a class and that classes functions with jest, when that class is a named export of an external library?
Edit:
mockGets definition:
const mockIpAndUrl ={
consumedPoints:1
};
const mockGet = jest.fn().mockImplementation(() => {
return mockIpAndUrl;
})
This does not work:
const mockIpAndUrl ={
consumedPoints:1
};
const mockGet = jest.fn().mockImplementation(() => {
return mockIpAndUrl;
})
jest.mock('rate-limiter-flexible', () => {
return{
RateLimiterRedis: jest.fn().mockImplementation(() => {
return { get : mockGet};
})
}
});
TypeError: limiter.get is not a function
However, this does:
jest.mock('rate-limiter-flexible', () => {
return{
RateLimiterRedis: jest.fn().mockImplementation(() => {
return { get : jest.fn().mockImplementation(() => {
return mockIpAndUrl;
})};
})
}
});
This is the documentation I was referring to:
https://jestjs.io/docs/en/es6-class-mocks#calling-jestmockdocsenjest-objectjestmockmodulename-factory-options-with-the-module-factory-parameter
This lead me to believe I could use mockGet
The export of rate-limiter-flexible is an object that is supposed to have RateLimiterRedis function that returns an instance that has get method.
"I have tried" snippet is wrong because it makes RateLimiterRedis function to return another function, and RateLimiterRedis itself isn't a spy so it cannot be asserted.
It should be:
jest.mock('rate-limiter-flexible', () => {
return {
RateLimiterRedis: jest.fn().mockImplementation(() => ({
get: mockGet
})
};
});
If RateLimiterRedis is instantiated in top-level imports, mockGet cannot be assigned before it's accessed inside a mock. It can be exported as a part of mocked module:
jest.mock('rate-limiter-flexible', () => {
const mockRateLimiterRedisGet = jest.fn();
return {
mockRateLimiterRedisGet,
RateLimiterRedis: jest.fn().mockImplementation(() => ({
get: mockRateLimiterRedisGet
})
};
});
This allows to import mockRateLimiterRedisGet from rate-limiter-flexible in a test and use for dynamically mocked values and assertions:
mockRateLimiterRedisGet.mockReturnValue(...);
...
expect(mockRateLimiterRedisGet).toBeCalled();

Jest spyOn() calls the actual function instead of the mocked

I'm testing apiMiddleware that calls its helper function callApi. To prevent the call to actual callApi which will issue the API call, I mocked the function. However, it still gets called.
apiMiddleware.js
import axios from 'axios';
export const CALL_API = 'Call API';
export const callApi = (...arg) => {
return axios(...arg)
.then( /*handle success*/ )
.catch( /*handle error*/ );
};
export default store => next => action => {
// determine whether to execute this middleware
const callAPI = action[CALL_API];
if (typeof callAPI === 'undefined') {
return next(action)
}
return callAPI(...callAPI)
.then( /*handle success*/ )
.catch( /*handle error*/ );
}
apiMiddleware.spec.js
import * as apiMiddleware from './apiMiddleware';
const { CALL_API, default: middleware, callApi } = apiMiddleware;
describe('Api Middleware', () => {
const store = {getState: jest.fn()};
const next = jest.fn();
let action;
beforeEach(() => {
// clear the result of the previous calls
next.mockClear();
// action that trigger apiMiddleware
action = {
[CALL_API]: {
// list of properties that change from test to test
}
};
});
it('calls mocked version of `callApi', () => {
const callApi = jest.spyOn(apiMiddleware, 'callApi').mockReturnValue(Promise.resolve());
// error point: middleware() calls the actual `callApi()`
middleware(store)(next)(action);
// assertion
});
});
Please ignore the action's properties and argument of callApi function. I don't think they are the concern of the point I'm trying to make.
Tell me if you need further elaboration.
The jest mocking only works on imported functions. In your apiMiddleware.js the default function is calling callApi variable, not the "exported" callApi function. To make the mock work, move callApi into its own module, and import it in apiMiddleware.js
Good question!
I solved my issues converting my code to a Class, example:
// Implementation
export class Location {
getLocation() {
const environment = this.getEnvironmentVariable();
return environment === "1" ? "USA" : "GLOBAL";
}
getEnvironmentVariable() {
return process.env.REACT_APP_LOCATION;
}
}
// Test
import { Location } from "./config";
test('location', () => {
const config = new Location();
jest.spyOn(config, "getEnvironmentVariable").mockReturnValue("1")
const location = config.getLocation();
expect(location).toBe("USA");
});

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