Mocking vuex action using and Mocha - unit-testing

I'm currently testing vuex module specifically actions.
Here's my code:
store/modules/users.js
export const state = () => ({
users: [],
})
export const mutations = () => ({
SET_USERS(state, users) {
console.log('Should reach Here');
state.users = users
}
})
export const actions = () => ({
getUsers({ commit }) {
return axios.get('/users')
.then(response => {
console.log('Reaching Here');
commit('SET_USERS', response.data.data.results)
})
.catch(error => {
console.log(error);
})
}
})
export const getters = () => {
users(state) {
return state.users;
}
};
Then when I test my actions:
tests/store/modules/users.js
it('should dispatch getUsers', () => {
mock.onGet('/users').reply(200, {
data: {
results: [
{ uid: 1, name: 'John Doe' },
{ uid: 2, name: 'Sam Smith' }
]
},
status: {
code: 200,
errorDetail: "",
message: "OK"
}
});
const commit = sinon.spy();
const state = {};
actions.getUsers({ commit, state });
expect(getters.users(state)).to.have.lengthOf(2);
});
when I try to run the test npm run dev it shows the console.log from action but from mutation SET_USERS it doesn't show the console.log
I'm referring to this documentation which I can use spy using sinon()
https://vuex.vuejs.org/guide/testing.html
How can I access the commit inside action to call mutation SET_USERS?

According to sinon docs
A test spy is a function that records arguments, return value, the value of this and exception thrown (if any) for all its calls. There are two types of spies: Some are anonymous functions, while others wrap methods that already exist in the system under test.
const commit = sinon.spy();
That is not the 'commit' from Vuex, you should test your mutation individually
actions.getUsers({ commit, state });
The commit argument is actually the spy, it will never trigger the mutation.
To test your mutation it could be something like this
mutations.SET_USERS(state, mockedUsers)
expect(state).to.have.lengthOf(mockedUsers.length)
...

Related

vitest failed to mock quasar

I am having vue3 app with vite and vitest and trying to mock the Quasar useQuasar composable which I am using in my custom Composable like:
// useLoginRequestBuilder.ts
import { makeUserAuthentication } from "#/main/factories"
import { useQuasar } from "quasar"
export function useLoginRequestBuilder() {
const $q = useQuasar()
async function login() {
try {
$q.loading.show()
const auth = makeUserAuthentication()
return await auth.signinRedirect()
} catch (e) {
console.log(e)
$q.loading.hide()
$q.notify({
color: "red-4",
textColor: "white",
icon: "o_warning",
message: "Login Failed!",
})
}
}
return {
login,
}
}
and I am trying to mock quasar in tests like:
// useLoginRequestBuilder.spec.ts
import { useLoginRequestBuilder } from "#/main/builders"
vi.mock("quasar", () => ({ // <--- this is not really mocking quasar
useQuasar: () => ({
loading: {
show: () => true,
hide: () => true,
},
}),
}))
const spyAuth = vi.fn(() => Promise.resolve(true))
vi.mock("#/main/factories", () => ({
makeUserAuthentication: () => ({
signinRedirect: () => spyAuth(),
}),
}))
describe("test useLoginRequestBuilder", () => {
test("should call signinRedirect", async () => {
const { login } = useLoginRequestBuilder()
const sut = await login()
expect(sut).toBe(true)
})
})
vi.mock("quasar"... is failing to mock quasar and I am getting below error. That means, it failed to mock and failed to get the $q.loading.... object.
TypeError: Cannot read properties of undefined (reading 'loading')
I understand that there is a separate testing lib for quasar, here but I think this is not really the case here.
Bordering on a necro-post, but I had a similar issue that the mocking factory wasn't creating the plugins being used in non-Vue components, and had to mock each call individually in the end.
Though I'd add it here for anyone else
vitest.mock("quasar", () => vi.fn()); // this doesn't mock out calls
// use individual mocks as below
import { Loading } from "quasar";
vi.spyOn(Loading, "show").mockImplementation(() => vi.fn());
vi.spyOn(Loading, "hide").mockImplementation(() => vi.fn());

Vue test utils incorrect value of computed

Hello I have checked the behaviour in application and it works with same data from api as I'm providing in mocked api call Api.contracts.getContractDetails.mockImplementationOnce(() => ({ data })); However, the value of hasWatermark computed is false - while it should be true.
How can I debug this? Is it possible to check computed in tests? This is my test:
function createWrapper() {
const i18n = new VueI18n({
locale: "en",
missing: jest.fn(),
});
return mount(EmployeeContract, {
i18n,
localVue,
store,
mocks: { $route: { query: {}, params: { id: "123" } }, $buefy: { toast: { open: jest.fn() } } },
stubs: ["spinner", "router-link", "b-switch"],
});
it("should add watermark for preview once it has rejected status", async () => {
const data = singleContract;
Api.contracts.getContractDetails.mockImplementationOnce(() => ({ data }));
const wrapper = createWrapper();
await flushPromises();
expect(wrapper.vm.hasWatermark).toBeTruthy();
});

Im trying to mock a function from a service but Jest keeps calling the actual function instead of the mock function

I'm using Jest to test a function from a service that uses axios to make some api calls. The problem is that Jest keeps calling the actual services function instead of the mocked service function. Here is all of the code:
The tests:
// __tests__/NotificationService.spec.js
const mockService = require('../NotificationService').default;
beforeEach(() => {
jest.mock('../NotificationService');
});
describe('NotificationService.js', () => {
it('returns the bell property', async () => {
expect.assertions(1);
const data = await mockService.fetchNotifications();
console.log(data);
expect(data).toHaveProperty('data.bell');
});
});
The mock:
// __mocks__/NotificationService.js
const notifData = {
bell: false,
rollups: [
{
id: 'hidden',
modifiedAt: 123,
read: true,
type: 'PLAYLIST_SUBSCRIBED',
visited: false,
muted: false,
count: 3,
user: {
id: 'hidden',
name: 'hidden'
},
reference: {
id: 'hidden',
title: 'hidden',
url: ''
}
}
],
system: [],
total: 1
};
export default function fetchNotifications(isResolved) {
return new Promise((resolve, reject) => {
process.nextTick(() =>
isResolved ? resolve(notifData) : reject({ error: 'It threw an error' })
);
});
}
The service:
import axios from 'axios';
// hardcoded user guid
export const userId = 'hidden';
// axios instance with hardcoded url and auth header
export const instance = axios.create({
baseURL: 'hidden',
headers: {
Authorization:
'JWT ey'
}
});
/**
* Notification Service
* Call these methods from the Notification Vuex Module
*/
export default class NotificationService {
/**
* #GET Gets a list of Notifications for a User
* #returns {AxiosPromise<any>}
* #param query
*/
static async fetchNotifications(query) {
try {
const res = await instance.get(`/rollups/user/${userId}`, {
query: query
});
return res;
} catch (error) {
console.error(error);
}
}
}
I've tried a couple of variations of using require instead of importing the NotificationService, but it gave some other cryptic errors...
I feel like I'm missing something simple.
Help me please :)
The problem is that Jest keeps calling the actual services function instead of the mocked service function.
babel-jest hoists jest.mock calls so that they run before everything else (even import calls), but the hoisting is local to the code block as described in issue 2582.
I feel like I'm missing something simple.
Move your jest.mock call outside the beforeEach and it will be hoisted to the top of your entire test so your mock is returned by require:
const mockService = require('../NotificationService').default; // mockService is your mock...
jest.mock('../NotificationService'); // ...because this runs first
describe('NotificationService.js', () => {
it('returns the bell property', async () => {
...
});
});

Testing vuex mocked mutation with jest

I have been testing my vuex store with the following structure
/src/store.js
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
const state = {
data: []
};
export const mutations = {
SET_DATA(state, data) {
state.data = data;
}
};
export const actions = {
fetchData({ commit }) {
fetch("https://jsonplaceholder.typicode.com/users")
.then(res => res.json())
.then(data => commit(SET_DATA, data))
.catch(err => console.log(err));
}
};
export default new Vuex.Store({
state,
mutations,
actions
});
My test file for actions asserts for the url and commit triggering
it("should fetch correct data and commit to store", () => {
// Mock fetch
global.fetch = jest.fn().mockImplementationOnce(() =>
Promise.resolve({
status: 200,
json: () => Promise.resolve(JSON.stringify(mockData))
})
);
return store.dispatch("fetchData").then(() => {
expect(global.fetch).toHaveBeenCalledWith(
"https://jsonplaceholder.typicode.com/users"
);
expect(mockSetData).toHaveBeenCalled();
});
});
Currently my test passes only for URL and does not call mockSetData which is a jest mocked function.
I'm new to TDD and not quite sure why this fails. Here's an implementation on codesandbox for more context

How do you test Collection.allow( ) functions that rely on the user ID?

Given the following collection and access control defintion
class TasksCollection extends Mongo.Collection {
insert (task, callback) {
const doc = _.extend({}, task, {
createdOn: new Date(),
owner: this.userId
})
super.insert(doc, callback)
}
}
export const Tasks = new TasksCollection('tasks')
// Simple checks to ensure that the user is logged in before making changes.
Tasks.allow({
insert: (userId, doc) =>=> !!userId,
update: (userId, doc, fields, modifier) => !!userId,
remove: (userId, doc) => !!userId
})
How would you test to ensure that it works using Mocha/Chai/Sinon? This is what I have tried.
import { Meteor } from 'meteor/meteor'
import { resetDatabase } from 'meteor/xolvio:cleaner';
import { assert, expect } from 'chai'
import { Tasks } from '/imports/api/tasks'
import sinon from 'sinon'
describe('collection test', () => {
beforeEach(() => {
resetDatabase()
})
it('can see a collection', () => {
assert(Tasks, 'unable to see sample collection')
})
it('can query an empty collection', () => {
expect(Tasks.find({}).fetch()).to.be.empty
})
it('fails to add to a collection when the user is not logged in', (done) => {
expect(Tasks.find({}).fetch()).to.be.empty
Tasks.insert({
text: 'hello world'
}, (error) => {
console.log('expected', error) // this is also a 404
assert(error)
done()
})
})
describe('logged in', () => {
let sandbox
beforeEach(() => {
sandbox = sinon.sandbox.create()
sandbox.stub(Meteor, 'userId').returns(42)
})
afterEach(() => {
sandbox.restore()
})
it('can add to a collection', (done) => {
expect(Tasks.find({}).fetch()).to.be.empty
Tasks.insert({
text: 'hello world'
}, (error, _id) => {
console.log(error)
assert(!error)
const results = Tasks.find({}).fetch()
expect(results).to.have.lengthOf(1)
expect(results[0].defaultValue).to.equal(42)
expect(results[0]._id).to.equal(_id)
expect(results[0].createdOn).to.not.be.undefined
done()
})
})
})
})
UPDATE: But I get a 404 error when calling the server.
The insecure package is already removed.
UPDATE: I am only testing on the client for now as the authorization can only be done from a client call.