vuejs unit test a component which has a child component - unit-testing

I want to test a TheLogin.vue component that has a child BaseInput.vue component. I tried the code below and also shallowMount but I keep getting the error below.
TheLogin.vue
<template>
<section>
<legend>
Hello Login
</legend>
<BaseInput id="userName"></BaseInput>
</section>
</template>
export default {
name: 'TheLogin',
data() {
return {
userName: null
}
}
}
TheLogin.spec.js
import TheLogin from '#/pages/login/TheLogin.vue';
import BaseInput from '#/components/ui/BaseInput.vue';
import { createLocalVue, mount } from '#vue/test-utils';
describe('TheLogin.vue', () => {
const localVue = createLocalVue();
localVue.use(BaseInput); // no luck
it('renders the title', () => {
const wrapper = mount(TheLogin, {
localVue,
// stubs: {BaseInput: true // no luck either
// stubs: ['base-input'] // no luck again
});
expect(wrapper.find('legend').text()).toEqual(
'Hello Login'
);
});
I import my base components in a separate file which I import into my main.js
import Vue from 'vue';
const components = {
BaseInput: () => import('#/components/ui/BaseInput.vue'),
BaseButton: () => import('#/components/ui/BaseButton.vue'),
//et cetera
};
Object.entries(components).forEach(([name, component]) =>
Vue.component(name, component)
);
The error I'm getting is:
TypeError: Cannot read property 'userName' of undefined
UPDATE
Turned out it was Vuelidate causing the error (the code above was not complete). I also had in my script:
validations: {
userName: {
required,
minLength: minLength(4)
},
password: {
required,
minLength: minLength(4)
}
}
I solved it by adding in my test:
import Vuelidate from 'vuelidate';
import Vue from 'vue';
Vue.use(Vuelidate);

Have you tried to shallow mount the component without using localVue and setting BaseInput as a stub?
Something like:
import TheLogin from '#/pages/login/TheLogin.vue';
import { shallowMount } from '#vue/test-utils';
describe('TheLogin.vue', () => {
it('renders the title', () => {
const wrapper = shallowMount(TheLogin, {
stubs: { BaseInput: true }
});
expect(wrapper.find('legend').text()).toEqual(
'Hello Login'
);
});
});

Related

Unit testing Vue component that fetches data and sends to component as prop

This Vue3 component contains a service that fetches an array of workshops from the database and passes this array as a prop to its child component(s). I want to make sure that the data is the expected data and in the expected form. How might I test this? If I mock this data, I wouldn't get that assurance. Here is my component WorkshopsView:
<script setup lang="ts">
import { useGetWorkshops } from '#/services/useGetWorkshops'
import WorkshopsGrid from '#/components/WorkshopsGrid.vue'
const { data: workshops, error: workshopsError } = useGetWorkshops()
</script>
<template>
<div>
<WorkshopsGrid :workshops="workshops" :workshopsError="workshopsError" />
</div>
</template>
Here is the service useGetWorkshops:
import axios from 'axios'
import useSWRV from 'swrv'
const useGetWorkshops = () =>
useSWRV('workshops', async () => {
const response = await axios.get(`/api/workshops`)
if (response.data.error) {
return null
}
return response.data.data
})
export default useGetWorkshops
Here is my Jest test file for WorkshopsView so far:
import { shallowMount } from '#vue/test-utils'
import WorkshopsView from '#/components/WorkshopsView.vue'
import WorkshopsGrid from '#/components/WorkshopsGrid.vue'
import useGetWorkshops from '#/services/useGetWorkshops'
jest.mock('#/services/useGetWorkshops', () => {
return jest.fn().mockImplementation(() => {
return { data: [], error: null }
})
})
describe('WorkshopsView', () => {
it('calls the child component', () => {
const wrapper = shallowMount(WorkshopsView)
expect(wrapper.findComponent(WorkshopsGrid).exists()).toBe(true)
})
it('calls the service to fetch the data', () => {
const wrapper = shallowMount(WorkshopsView)
expect(useGetWorkshops).toHaveBeenCalled()
}
}
How might it be tested that the component is getting the right data in the expected form to pass to the child component as a prop?

Run unit test with Jest, Vue, Vuetify, and Pug

I am currently working on a project that is using Vue, Class based components, typescript, pug, vuetify and Jest for unit testing. I have been trying to run unit tests using jest and have not been able to get them to work. At this point I am pretty lost as to what could be wrong. It seems that there are issues with unit tests when using vueifty which I think I have sorted out but am not certain. When I run the test the test fails because the wrapper is always empty.
Component
<template lang="pug">
v-row(align="center" justify="center")
v-col(cols="6")
v-card
v-form(ref="loginForm" v-model="valid" v-on:keyup.enter.native="login")
v-card-title#title Login
v-card-text
v-text-field(class="mt-4" label="Username" required outlined v-model="username" :rules="[() => !!username || 'Username Required.']")
v-text-field(label="Password" required outlined password :type="show ? 'text' : 'password'" :append-icon="show ? 'visibility' : 'visibility_off'" #click:append="show = !show" v-model="password" :rules="[() => !!password || 'Password Required.']")
v-alert(v-if="error" v-model="error" type="error" dense dismissible class="mx-4")
| Error while logging in: {{ errorMsg }}
v-card-actions()
div(class="flex-grow-1")
v-btn(class="mr-4" color="teal" :disabled="!valid" large depressed #click="login") Login
div Forgot password?
a(href="/forgot-password" class="mx-2") Click here
div(class="my-2") Don't have an account?
a(href="/signup" class="mx-2") Signup
| for one
</template>
<script lang="ts">
import { AxiosError, AxiosResponse } from 'axios';
import JwtDecode from 'jwt-decode';
import { Component, Vue } from 'vue-property-decorator';
import { TokenDto, VForm } from '#/interfaces/GlobalTypes';
#Component({
name: 'LoginForm',
})
export default class Login extends Vue {
private password: string = '';
private username: string = '';
private show: boolean = false;
private error: boolean = false;
private errorMsg: string = '';
private valid: boolean = false;
... removed rest for brevity
Test
import LoginForm from '#/components/auth/LoginForm.vue';
import login from '#/views/auth/LoginView.vue';
import { createLocalVue, mount } from '#vue/test-utils';
import Vue from 'vue';
import Vuetify from 'vuetify';
// jest.mock('axios')
Vue.use(Vuetify)
const localVue = createLocalVue();
console.log(localVue)
describe('LoginForm.vue', () => {
let vuetify: any
beforeEach(() => {
vuetify = new Vuetify()
});
it('should log in successfully', () => {
const wrapper = mount(LoginForm, {
localVue,
vuetify
})
console.log(wrapper.find('.v-btn'))
});
});
The LoginForm is loaded properly but it does not seeem that that mount creates the wrapper for some reason. When I log the wrapper I get:
VueWrapper {
isFunctionalComponent: undefined,
_emitted: [Object: null prototype] {},
_emittedByOrder: []
}
Any ideas are greatly appericated
you can try:
wrapper.findComponent({name: 'v-btn'})
I guess I am late but I made it work.
I noticed you tried to find VBtn component by 'v-btn' class but VBtn doesn't have it by default. That's why I decided to stub it with my own VBtn that has 'v-btn' class.
import { shallowMount, Wrapper } from '#vue/test-utils'
import Login from '#/components/Login/Login.vue'
import Vue from 'vue'
import Vuetify from 'vuetify'
Vue.use(Vuetify)
let wrapper: Wrapper<Login & { [ key: string]: any }>
const VButtonStub = {
template: '<button class="v-btn"/>'
}
describe('LoginForm.vue', () => {
beforeEach(() => {
wrapper = shallowMount(Login, {
stubs: {
VBtn: VButtonStub
}
})
})
it('should log in successfully', () => {
console.log(wrapper.html())
})
})
After test passed you will see in console log that stubbed component has 'v-btn' class. You can add yours and work with it like you want.

Testing VueJS component containing other components written by myself

I work on a website with multiple components that contain other components. Now I would like to test if the save button of a form is deactivated correctly if no data is set. I am using vuetify for the UI and Jest for testing.
Here is my parent component, containing the edit-user-details component:
<template>
<v-container>
<v-form v-model="valid">
<v-card>
<v-card-text>
<edit-user-details :user="user"></edit-user-details>
</v-card-text>
<v-card-actions>
<v-btn :disabled="!valid" #click="save()">Save</v-btn>
<v-btn #click="cancel()">Cancel</v-btn>
</v-card-actions>
</v-card>
</v-form>
</v-container>
</template>
<script>
export default {
name: "edit-user",
components: {},
data: () => ({
user: {},
valid: false
}),
methods: {
save() {
...
},
cancel() {
...}
}
}
}
</script>
This is a part of the edit-user-details component:
<template>
<v-container>
<v-text-field
v-model="user.userName"
label="Username*"
required
:rules="[v => !!v || 'Please, enter a user name.']"
></v-text-field>
...
</v-container>
</template>
<script>
export default {
name: "edit-user-details",
props: {
user: {
type: Object,
default: () => ({})
}
}
}
</script>
And here we have my test:
import { mount, createLocalVue } from '#vue/test-utils'
import EditUser from '../../src/views/EditUser'
import Vuetify from 'vuetify';
import EditUserDetails from '../../src/components/EditUserDetails'
describe('Edited user data ', () => {
it('can be saved if valid', () => {
const localVue = createLocalVue();
localVue.use(Vuetify)
localVue.use(EditUserDetails)
const wrapper = mount(EditUser, {
localVue: localVue
});
})
})
The test is green because it has no assert. The main issue is, that I get this error: [Vue warn]: Unknown custom element: - did you register the component correctly? For recursive components, make sure to provide the "name" option.
So my question is: How can I test a component containing other components written by me?
Thank you in advance for your help.
instead of mount, use shallowMount.
Like mount, it creates a Wrapper that contains the mounted and
rendered Vue component, but with stubbed child components.
https://vue-test-utils.vuejs.org/api/#shallowmount
I haven’t tried this together with createLocalVue(), but I hope it'll help:
import Vuetify from 'vuetify'
const vuetify = new Vuetify()
const wrapper = mount(Component, { ..., vuetify })

How to unit test a method of react component?

I am trying to unit test my reactjs component:
import React from 'react';
import Modal from 'react-modal';
import store from '../../../store'
import lodash from 'lodash'
export class AddToOrder extends React.Component {
constructor(props) {
super(props);
this.state = {checked: false}
//debugger
}
checkBoxChecked() {
return true
}
render() {
console.log('testing=this.props.id',this.props.id )
return (
<div className="order">
<label>
<input
id={this.props.parent}
checked={this.checkBoxChecked()}
onChange={this.addToOrder.bind(this, this.props)}
type="checkbox"/>
Add to order
</label>
</div>
)
}
}
export default AddToOrder;
Just to get started I am already struggling to assert the checkBoxChecked method:
import React from 'react-native';
import {shallow} from 'enzyme';
import {AddToOrder} from '../app/components/buttons/addtoorder/addtoorder';
import {expect} from 'chai';
import {mount} from 'enzyme';
import jsdom from 'jsdom';
const doc = jsdom.jsdom('<!doctype html><html><body></body></html>')
global.document = doc
global.window = doc.defaultView
let props;
beforeEach(() => {
props = {
cart: {
items: [{
id: 100,
price: 2000,
name:'Docs'
}]
}
};
});
describe('AddToOrder component', () => {
it('should be handling checkboxChecked', () => {
const wrapper = shallow(<AddToOrder {...props.cart} />);
expect(wrapper.checkBoxChecked()).equals(true); //error appears here
});
});
```
How can I unit test a method on the component? This is the error I am getting:
TypeError: Cannot read property 'checked' of undefined
You are almost there. Just change your expect to this:
expect(wrapper.instance().checkBoxChecked()).equals(true);
You can go through this link to know more about testing component methods using enzyme
For those who find the accepted answer as not working, try using .dive() on your shallow wrapper before using .instance():
expect(wrapper.dive().instance().somePrivateMethod()).toEqual(true);
Reference: Testing component methods with enzyme
Extend of previous answer.
If you have connected component (Redux) , try next code :
const store=configureStore();
const context = { store };
const wrapper = shallow(
<MyComponent,
{ context },
);
const inst = wrapper.dive().instance();
inst.myCustomMethod('hello');

Redux: How to test a connected component?

I am using Enzyme to unit test my React components. I understand that in order to test the raw unconnected component I'd have to just export it and test it (I've done that). I have managed to write a test for the connected component but I am really not sure if this's the right way and also what exactly would I want to test for the connected component.
Container.jsx
import {connect} from 'react-redux';
import Login from './Login.jsx';
import * as loginActions from './login.actions';
const mapStateToProps = state => ({
auth: state.auth
});
const mapDispatchToProps = dispatch => ({
loginUser: credentials => dispatch(loginActions.loginUser(credentials))
});
export default connect(mapStateToProps, mapDispatchToProps)(Login);
Container.test.js
import React from 'react';
import {Provider} from 'react-redux';
import {mount, shallow} from 'enzyme';
import {expect} from 'chai';
import LoginContainer from '../../src/login/login.container';
import Login from '../../src/login/Login';
describe('Container Login', () => {
it('should render the container component', () => {
const storeFake = state => ({
default: () => {
},
subscribe: () => {
},
dispatch: () => {
},
getState: () => ({ ...state })
});
const store = storeFake({
auth: {
sport: 'BASKETBALL'
}
});
const wrapper = mount(
<Provider store={store}>
<LoginContainer />
</Provider>
);
expect(wrapper.find(LoginContainer).length).to.equal(1);
const container = wrapper.find(LoginContainer);
expect(container.find(Login).length).to.equal(1);
expect(container.find(Login).props().auth).to.eql({ sport: 'BASKETBALL' });
});
});
This is an interesting question.
I usually do import both container and component to do the testing. For container testing I use, redux-mock-store. Component testing is for testing async functions. For instance in your case, login process is an async function using sinon stubs. Here is a snippet of the same,
import React from 'react';
import {Provider} from 'react-redux';
import {mount, shallow} from 'enzyme';
import {expect} from 'chai';
import LoginContainer from '../../src/login/login.container';
import Login from '../../src/login/Login';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import { stub } from 'sinon';
const mockStore = configureMockStore([thunk]);
describe('Container Login', () => {
let store;
beforeEach(() => {
store = mockStore({
auth: {
sport: 'BASKETBALL',
},
});
});
it('should render the container component', () => {
const wrapper = mount(
<Provider store={store}>
<LoginContainer />
</Provider>
);
expect(wrapper.find(LoginContainer).length).to.equal(1);
const container = wrapper.find(LoginContainer);
expect(container.find(Login).length).to.equal(1);
expect(container.find(Login).props().auth).to.eql({ sport: 'BASKETBALL' });
});
it('should perform login', () => {
const loginStub = stub().withArgs({
username: 'abcd',
password: '1234',
});
const wrapper = mount(<Login
loginUser={loginStub}
/>);
wrapper.find('button').simulate('click');
expect(loginStub.callCount).to.equal(1);
});
});
As you pointed out, the way I usually do this is to export the un-connected component as well, and test that.
i.e.
export {Login};
Here's an example. Source of the component, and source of the tests.
For the wrapped component, I don't author tests for those because my mappings (mapStateToProps and mapDispatchToProps) are generally very simple. If I wanted to test a wrapped component, I'd really just be testing those maps. So those are what I would choose to explicitly test, rather than re-testing the entire component in a wrapped form.
There are two ways to test those functions. One way would be to export the functions within the module itself.
i.e.;
export {mapStateToProps, mapDispatchToProps}
I'm not a huge fan of this, because I wouldn't want other modules in the app to access them. In my tests, I sometimes use babel-plugin-rewire to access "in-scope" variables, so that's what I would do in this situation.
That might look something like:
import {
Login, __Rewire__
}
const mapStateToProps = __Rewire__.__get__('mapStateToProps');
describe('mapStateToProps', () => { ... });
If we have a router issue, we can consider to add the router lib into the test file, eg:
import React from 'react';
import { Provider } from 'react-redux';
import { BrowserRouter as Router } from 'react-router-dom';
import { mount } from 'enzyme';
import ReadDots from './ReadDots';
const storeFake = state => ({
default: () => {
},
subscribe: () => {
},
dispatch: () => {
},
getState: () => ({ ...state })
});
const store = storeFake({
dot: {
dots: [
{
id: '1',
dot: 'test data',
cost: '100',
tag: 'pocket money'
}
]
}
});
describe('<ReadDots />', () => {
it('should render ReadDots component', () => {
const component = mount(
<Provider store={store}>
<Router>
<ReadDots />
</Router>
</Provider>
);
expect(component.length).toEqual(1);
});
});