How can I pass a boolean to an expect statement for testing using mocha/chai? - unit-testing

Using Vue CLI I have a unit test that I am trying to check for a true/false that looks like this:
describe('The thing', () => {
it('must be available.', () => {
const available = true
const wrapper = shallowMount(MyVueComponent, {
propsData: { available },
})
expect(wrapper).to.be.true
})
})
When I run npm run test:unit
I get the following:
AssertionError: expected { Object (isFunctionalComponent, _emitted, ...) } to be true
If I just check the value of available, then it's all good. But that seems like I'm doing it wrong.
Other tests I have written are working fine as I am checking for a text value:
describe('The thing', () => {
it('should have a name.', () => {
const name = 'Hal'
const wrapper = shallowMount(MyVueComponent, {
propsData: { name },
})
expect(wrapper.text()).to.include(name)
})
})
I am not sure how to check that the available is a boolean and it must be true. Any suggestions would be appreciated!
EDIT
This is what my Vue component looks like:
export default {
name: 'MyVueComponent',
props: {
name: String
},
data: function() {
return {
available: true,
}
},
}
EDIT 2
This seems to work in my unit test:
describe('The thing', () => {
it('must be available.', () => {
const available = true
const wrapper = shallowMount(MyVueComponent, {
propsData: { available },
})
expect(wrapper.vm.available).to.be.true
})
})
However, it is looking at my actual component in my /src directory. If I change the data values from true to false my tests come out correctly. I'm not sure how to have the data stay at the test level. So if I were to change const available = false, my test should fail--but it does not.
EDIT 3
It seems like this works (to access the data object):
describe("The thing", () => {
it("must be available.", () => {
const defaultData = MyVueComponent.data();
// const wrapper = shallowMount(MyVueComponent, {});
expect(defaultData.available).to.be.true;
});
});
But it still seems not right that I'm referencing my actual code, and not sticking within the unit tests.

You want to check the received prop, which you can do with wrapper.props()
describe('The thing', () => {
it('must be available.', () => {
const available = true
const wrapper = shallowMount(MyVueComponent, {
propsData: { available },
})
expect(wrapper.props().available).to.be.true
// or:
// expect(wrapper.props().available).to.equal(available)
})
})
Chai's .to.be.true and .to.equal use === so there is no need to separately check that it is indeed a Boolean, but if you prefer the "expressiveness" of it, you can check it too:
expect(wrapper.props().available).to.be.a('boolean')

Related

Vitest gives no output

I'm new to testing and im trying to write some unit tests for my Vue app. The problem is that vitest givesno output and I cant figure out what is wrong. Any help would be apriciated.
describe('UserForm', () => {
it('renders component properly', async () => {
const viewId = "123"
render(UserForm, {
props: {
open: true
}
})
const view = await screen.findByText('Kontrahent')
expect(view.id).toBe(viewId)
})
})
I run the test with this command
vitest --environment jsdom
Have you tried to do something like:
import { mount } from "#vue/test-utils";
// in the describe
const wrapper = mount(Login, {
props: {
open: true
},
});
it("mounts the component", () => {
expect(wrapper.html()).toContain("Kontrahent");
});

Mocking axios in a vue3 typescript unit test using jest and vue-test-utils2 (Solved)

My component calls
this.axios.get()
when being mounted and passes a vuex-store variable to the api. The api returns an array as the response and the component displays some of the returned data after exchanging a loading-element with the real content.
In my unit test I want to simulate the result of the axios-request, wait for the transition between the loading- and the content-element and then finally check the validity of the content. However, the test fails and outputs:
Cannot read property 'get' of undefined
and highlights the get on this.axios.
Here is what I'm expecting to work (based on this guide):
... some imports etc. ...
const mockAxios = { whatIExpectToGet };
jest.mock("axios", () => ({
get: jest.fn(() => mockAxios)
}));
it("description of the test", async () => {
const wrapper = mount(MyComponent);
... code continues ...
Of course I'm accesssing axios via this and not directly like they do in the guide. But, since I can't find any mention of anything related to that, I assume that's irrelevant?
I also tried to mock axios myself like so:
... imports etc. ...
const axios = {
get: Promise.resolve({ whatIExpectToGet })
};
it("description of the test", async () => {
const wrapper = mount(MyComponent, {
global: {
mocks: [ axios ]
}
});
... code continues ...
Apparently people with similar problems used localVue.use() to inject stuff, but that's no longer supported.
Could someone be so kind and smart as to point me into the right direction, please?
Thank you.
-------------------> SOLUTION <-------------------
Thanks to tony 19 this question is already solved.
I ended up using an async function to mock axios because Promise.resolve() wasn't working for me:
import { shallowMount, flushPromises } from "#vue/test-utils";
import MyComponent from "#/components/MyComponent.vue";
describe("MyComponent.vue", () => {
const axios = {
get: async () => ({
data: { expectedData }
})
};
it("test description", async () => {
const wrapper = shallowMount(MyComponent, {
global: {
mocks: {
axios: axios
}
}
} as any);
expect(wrapper.html()).toContain("some_string_i_display_while_loading");
await flushPromises();
expect(wrapper.html()).toContain("some_string_i_display_after_getting_the_response");
});
});
Using global.mocks to mock axios is the right approach, but your attempt incorrectly used an array when it should've been an object:
const wrapper = mount(MyComponent, {
global: {
// mocks: [ axios ] ❌
mocks: { axios } ✅
}
})
Note axios.get() resolves to an axios.Response object, which stores the response data in its data property, so your mock should do the same.
Here's a full example:
// MyComponent.vue
export default {
mounted() {
this.axios.get('foo').then(resp => this.foo = resp.data)
}
}
// MyComponent.spec.js
it('gets foo', () => {
const wrapper = mount(MyComponent, {
global: {
mocks: {
axios: {
get: Promise.resolve({ data: { foo: true }})
// OR use an async function, which internally returns a Promise
get: async () => ({ data: { foo: true }})
}
}
}
}
})

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();
});

Mocking vuex action using and Mocha

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)
...

Why does VueX store retain state across multiple unit tests?

I am writing unit tests for a pagination module, and it has a simple VueX store module.
I am using Vue.js 2.5 and Mocha/Chai/Sinon for tests. Setup is with Vue CLI 3.
The problem is that when the currentPage is incremented in the store in one unit test, this state persists into the next test even when I try to create a fresh store.
I have attempted to return a fresh pagination module by using a function that returns an Object.assign() fresh copy but this did not work. I have left this in the code as shown in the spec below.
store/pagination.js
const state = {
currentPage: 0
}
export const getters = {
currentPage: state => {
return state.currentPage
}
}
export const actions = {
nextPage ({ commit, state }) {
commit('setCurrentPage', state.currentPage + 1)
}
}
export const mutations = {
setCurrentPage (state, page) {
state.currentPage = page
}
}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}
Pagination.spec.js
function getPaginationStore () {
return Object.assign({}, pagination)
}
describe('Paginate.vue', () => {
let localVue
let wrapper
let store
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
modules: {
pagination: getPaginationStore()
}
})
wrapper = shallowMount(Pagination, {
localVue,
propsData: {
items: [],
size: 24
},
store
})
})
afterEach(() => {
store = null
})
it('state should be 0', () => {
expect(wrapper.vm.pageNumber).to.equal(0)
wrapper.vm.$store.dispatch('pagination/nextPage')
expect(wrapper.vm.pageNumber).to.equal(1)
})
it('state should be 0 again but is 1', () => {
// THIS TEST FAILS. IT IS ACTUALLY 1
expect(wrapper.vm.pageNumber).to.equal(0)
})
})
The solution was to use a function for the state in the module rather than a plain old javascript object. Here is my new store state code:
export const state = () => {
return {
currentPage: 0
}
}
Answer was provided by #SumNeuron from the Vue discord channel.