Unit testing a Vue plugin - javascript

I have a plugin that console.logs data.
logData.spec.js
import Vue from 'vue'
import { createLocalVue } from '#vue/test-utils'
import logData from './logData'
describe('logData plugin', () => {
const localVue = createLocalVue()
it('adds a $logData method to the Vue prototype', () => {
expect(Vue.prototype.$logData).toBeUndefined()
localVue.use(logData)
expect(typeof localVue.prototype.$logData).toBe('function')
})
it('console.logs data passed to it', () => {
const data = 'data to be logged'
const localVue = createLocalVue()
localVue.use(logData)
expect(localVue.prototype.$logData(data)).toBe('data to be logged')
})
})
logData.js
export function logData (dataToLog) {
const isLoggingData = localStorage.getItem('isLoggingData')
if (isLoggingData) {
console.log(dataToLog)
}
}
export default {
install: function (Vue) {
Vue.prototype.$logData = logData
}
}
The error I get is in my unit test is Expected: 'data to be logged", Received: undefined. Why is the second test being read as undefined?

It's expected behavior since console.log() returns undefined. To get desired result you should add this line of code to your lodData function:
return dataToLog
export function logData (dataToLog) {
const isLoggingData = localStorage.getItem('isLoggingData')
if (isLoggingData) {
console.log(dataToLog)
return dataToLog
}
}
NOTICE: Also you don't have localStorage in your test environment.

Related

Testing Vuejs EventBus

I have been trying to figure this out for some time already, but I can not make it work! I found some examples on internet, but nothing solves it, every time I run my test I get:
Expected number of calls: 1
Received number of calls: 0
> 186 | expect(wrapper.vm.EventBus.$on).toHaveBeenCalledTimes(1);
The component looks like this:
import {EventBus} from 'eventbus'
export default{
data(){ return {}},
mounted(){
EventBus.$on('saveTerminal', function(){
this.someOtherFunction()
})
}
}
Event Bus file looks like this
import Vue from 'vue';
export const EventBus = new Vue();
The Test looks like this:
const GlobalPlugins = {
install(v) {
v.prototype.EventBus = new Vue();
},
};
//Vue.prototype.EventBus = new Vue(); <-- I tried this also, didn't do anything
// Mounting component
const $t = () => {}
const params = { localVue, store, router,
propsData: {
isEdit: false
},
data(){
return {
loading: false,
tabIndex: 1
};
},
mocks:{
$t,
EventBus: {
$on: jest.fn(),
$off: jest.fn(),
$emit: jest.fn()
}
},
}
const wrapper = shallowMount(MyComponent, params)
describe('My component', () => {
it('Event bus', () => {
wrapper.vm.EventBus.$emit('saveTerminal');
expect(wrapper.vm.EventBus.$on).toHaveBeenCalledTimes(1);
expect(wrapper.vm.EventBus.$on).toHaveBeenCalledWith('saveTerminal', expect.any(Function))
});
})
You can use jest.mock() to mock the EventBus module. Your test would require() the module to access the mock, and then verify its $on was called:
import { shallowMount } from '#vue/test-utils'
import MyComponent from '#/components/MyComponent.vue'
jest.mock('#/utils/EventBus')
describe('MyComponent.vue', () => {
it(`listens to 'saveTerminal' upon mounting`, async () => {
const { EventBus } = require('#/utils/EventBus')
shallowMount(MyComponent)
expect(EventBus.$on).toHaveBeenCalledWith('saveTerminal', expect.any(Function))
})
})
demo

TypeError: (0 , _testUtils.createLocalVue) is not a function

This is my code. Can some please help me figure out the error.I am using jest to test out my frontend which I have built using Vue.The line const localVue = createLocalVue(); is giving out the error TypeError: (0 , _testUtils.createLocalVue) is not a function
import { createLocalVue,shallowMount } from '#vue/test-utils'
import Vuex from 'vuex'
import getters from '../../src/store/module/auth/getters.js'
import TheHeader from '#/components/layout/TheHeader.vue'
// const store = new Vuex.Store({
// state: {
// user:null,
// token:'',
// expiresIn:null,
// isUserLoggedIn:false,
// isAdminLoggedIn:false,
// }
// })
describe('TheHeader', () => {
const localVue = createLocalVue();
localVue.use(Vuex);
let store
let state
it('Checks whether the login is correctly displayed', () => {
const cmp = shallowMount(TheHeader, { store,localVue})
expect(cmp.name()).toMatch('TheHeader')
expect(cmp.vm.isLoggedIn()).toBe(false)
})
})
createLocalVue was removed in version 2 of #vue/test-utils, which explains why it's undefined in your example.
To install a Vue plugin (such as Vuex), use the global.plugins mounting option
To mock instance APIs (such as this.$store), use the global.mocks mounting option
import Vuex from 'vuex'
import { shallowMount } from '#vue/test-utils'
import TheHeader from '#/components/TheHeader.vue'
const store = /* Vuex store */
const cmp = shallowMount(TheHeader, {
global: {
plugins: [Vuex],
// OR:
mocks: {
$store: store,
}
}
})
import { mount } from '#vue/test-utils'
import { createApp } from 'vue'
import { createStore } from 'vuex'
import App from '#/views/Home'
creating a fake store
const store = createStore({
state() {
return {
count: 0,
user: {},
}
},
mutations: {
increment(state) {
state.count += 1
},
},
})
Creating the Component
const app = createApp(App)
app.use(store)
let wrapper
beforeEach(() => {
wrapper = mount(App, {
global: {
plugins: [store],
},
computed: { showAlert: () => false },
})
})
now you can do the test
test('Home', async () => {
expect(wrapper.vm.showAlert).toBe(false)
})

Jest mock async function from default import

I'm trying to mock an async function that is exported as a default export but all I get is TypeError: Cannot read property 'then' of undefined
What I'm trying to mock is config.js:
const configureEnvironment = async (nativeConfig) => {
return { await whatever() }
}
The file I'm testing is Scene.js:
import configureEnvironment from './config';
class Scene extends React.Component {
constructor(props) {
nativeConfig = {};
configureEnfironment(nativeConfig).then((config) => {
// Do stuff
}
}
}
And my test file is Scene.test.js:
let getScene = null;
const configureEnvironmentMock = jest.fn();
describe('Scene', () => {
jest.mock('./config', () => configureEnvironmentMock);
const Scene = require('./Scene').default;
getScene = (previousState) => {
return shallow(
<Scene prevState={previousState}>
<Fragment />
</Scene>,
);
};
it('calls configureEnvironment with the nativeConfig', async () => {
expect.assertions(1);
const nativeConfig = {};
getScene(nativeConfig);
expect(configureEnvironmentMock).toHaveBeenCalledWith(nativeConfig);
});
});
However, the result of running the test is:
TypeError: Cannot read property 'then' of undefined
I understand the issue is on the way I mock configureEnvironment but I cannot get it working.
I also tried to mock the function like:
jest.mock('./config', () => {
return {
default: configureEnvironmentMock,
};
});
But it results on:
TypeError: (0 , _config2.default) is not a function
A clean and simple way to mock the default export of a module is to use jest.spyOn in combination with functions like mockImplementation.
Here is a working example based on the code snippets above:
config.js
const whatever = async () => 'result';
const configureEnvironment = async (nativeConfig) => await whatever();
export default configureEnvironment;
Scene.js
import * as React from 'react';
import configureEnvironment from './config';
export class Scene extends React.Component {
constructor(props) {
super(props);
configureEnvironment(props.prevState).then((config) => {
// Do stuff
});
}
render() {
return null;
}
}
Scene.test.js
import React, { Fragment } from 'react';
import { shallow } from 'enzyme';
import { Scene } from './Scene';
import * as config from './config';
describe('Scene', () => {
const mock = jest.spyOn(config, 'default'); // spy on the default export of config
mock.mockImplementation(() => Promise.resolve('config')); // replace the implementation
const getScene = (previousState) => {
return shallow(
<Scene prevState={previousState}>
<Fragment />
</Scene>,
);
};
it('calls configureEnvironment with the nativeConfig', async () => {
expect.assertions(1);
const nativeConfig = {};
getScene(nativeConfig);
expect(mock).lastCalledWith(nativeConfig); // SUCCESS
});
});
You can mock anything with jest, like this
jest.mock('#material-ui/core/withWidth', () => ({
__esModule: true,
isWidthUp: jest.fn((a, b) => true),
default: jest.fn(fn => fn => fn)
}))

`TypeError: store.dispatch(...).then is not a function` when trying to test async actions

Trying to test my async action creators using this example: http://redux.js.org/docs/recipes/WritingTests.html#async-action-creators and I think I did everything the same but I've got an error:
async actions › creates FETCH_BALANCE_SUCCEESS when fetching balance has been done
TypeError: store.dispatch(...).then is not a function
Don't understand why it's happened because I did everything from the example step by step.
I also found this example http://arnaudbenard.com/redux-mock-store/ but anyway, mistake exists somewhere and unfortunately, I can't find it. Where is my mistake, why I've got an error even If my test case looks like the same as an example
My test case:
import nock from 'nock';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import * as actions from './';
import * as types from '../constants';
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
describe('async actions', () => {
afterEach(() => {
nock.cleanAll();
});
it('creates FETCH_BALANCE_SUCCEESS when fetching balance has been done', () => {
const store = mockStore({});
const balance = {};
nock('http://localhost:8080')
.get('/api/getbalance')
.reply(200, { body: { balance } });
const expectedActions = [
{ type: types.FETCH_BALANCE_REQUEST },
{ type: types.FETCH_BALANCE_SUCCEESS, body: { balance } },
];
return store.dispatch(actions.fetchBalanceRequest()).then(() => {
// return of async actions
expect(store.getActions()).toEqual(expectedActions);
});
});
});
My actions which I am trying to test.
import 'whatwg-fetch';
import * as actions from './actions';
import * as types from '../constants';
export const fetchBalanceRequest = () => ({
type: types.FETCH_BALANCE_REQUEST,
});
export const fetchBalanceSucceess = balance => ({
type: types.FETCH_BALANCE_SUCCEESS,
balance,
});
export const fetchBalanceFail = error => ({
type: types.FETCH_BALANCE_FAIL,
error,
});
const API_ROOT = 'http://localhost:8080';
const callApi = url =>
fetch(url).then(response => {
if (!response.ok) {
return Promise.reject(response.statusText);
}
return response.json();
});
export const fetchBalance = () => {
return dispatch => {
dispatch(actions.fetchBalanceRequest());
return callApi(`${API_ROOT}/api/getbalance`)
.then(json => dispatch(actions.fetchBalanceSucceess(json)))
.catch(error =>
dispatch(actions.fetchBalanceFail(error.message || error))
);
};
};
In your test you have
return store.dispatch(actions.fetchBalanceRequest()).then(() => { ... })
You're trying to test fetchBalanceRequest, which returns an object, so you cannot call .then() on that. In your tests, you would actually want to test fetchBalance, since that is an async action creator (and that is what is explained in the redux docs you posted).
That's usually a problem with redux-mock-store
Remember that:
import configureStore from 'redux-mock-store'
The function configureStore does not return a valid store, but a factory.
Meaning that you have to call the factory to get the store:
const store = configureStore([])()

proxyquire TypeError when not replacing every function in a module

I'm trying to use proxyquire to unit test my Redux reducers. I need to replace the functionality of one function in my test but keep the original functionality of the other, which is possible according to proxyquire's docs.
formsReducer.test.js:
import { expect } from 'chai';
import * as types from '../constants/actionTypes';
import testData from '../data/TestData';
import proxyquire from 'proxyquire';
describe('Forms Reducer', () => {
describe('types.UPDATE_PRODUCT', () => {
it('should get new form blueprints when the product changes', () => {
//arrange
const initialState = {
blueprints: [ testData.ipsBlueprint ],
instances: [ testData.basicFormInstance ]
};
//use proxyquire to stub call to formsHelper.getFormsByProductId
const formsReducerProxy = proxyquire.noCallThru().load('./formsReducer', {
'../utils/FormsHelper': {
getFormsByProductId: () => { return initialState.blueprints; }
}
}).default;
const action = {
type: types.UPDATE_PRODUCT,
stateOfResidence: testData.alabamaObject,
product: testData.basicProduct
};
//act
const newState = formsReducerProxy(initialState, action);
//assert
expect(newState.blueprints).to.be.an('array');
expect(newState.blueprints).to.equal(initialState.blueprints);
});
});
});
formsReducer.js:
import * as types from '../constants/actionTypes';
import objectAssign from 'object-assign';
import initialState from './initialState';
import formsHelper from '../utils/FormsHelper';
export default function formsReducer(state = initialState.forms, action) {
switch (action.type) {
case types.UPDATE_PRODUCT: {
let formBlueprints = formsHelper.getFormsByProductId(action.product.id);
formBlueprints = formsHelper.addOrRemoveMnDisclosure(formBlueprints, action.stateOfResidence.id);
return objectAssign({}, state, {blueprints: formBlueprints, instances: []});
}
}
I need to replace the functionality of formsHelper.getFormsByProductId() but keep the original functionality of formsHelper.addOrRemoveMnDisclosure() - as you can see in the proxyquire block I'm only replacing the getFormsByProductId() function. However, when I do this get the following error: TypeError: _FormsHelper2.default.addOrRemoveMnDisclosure is not a function. Looks to be a problem either with babel or with my export default for FormHelper.
The export for the FormsHelper looks like this:
export default class FormsHelper { ...methods and whatnot }.
How can I fix this problem?

Categories

Resources