I am using nuxt with jest to test my axios isit working but i keep having this error which i not sure what happen.
AppNotifications.vue
<template>
<div>
<li v-for="notification in notifications" :key="notification.id">
{{ notification.body }}
</li>
</div>
</template>
<script>
export default {
data() {
return {
notifications: []
}
},
methods: {
async takeNotifications() {
let response = await this.$axios.$get('/notifications.json')
console.log(response)
// this.notifications = response.data.data
}
},
mounted() {
this.takeNotifications()
}
}
</script>
AppNotifications.spec.js
import {
mount
}
from '#vue/test-utils'
import AppNotifications from '#/components/AppNotifications.vue'
import axios from 'axios'
jest.mock('axios', () => {
return {
$get: jest.fn(() => Promise.resolve({
name: 'alex'
}))
}
})
describe('AppNotifications', () => {
it('renders a list of notifications', () => {
let wrapper = mount(AppNotifications)
})
})
Error call out test:
Determining test suites to run...[warn] `mode` option is deprecated. Please use `ssr: true` for universal mode or `ssr: false` for spa mode and remove `mode` from `nuxt.config`
RUNS __tests__/AppNotifications.spec.js
node:internal/process/promises:246
triggerUncaughtException(err, true /* fromPromise */);
^
[UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "TypeError: Cannot read properties of undefined (reading '$get')".] {
code: 'ERR_UNHANDLED_REJECTION'
}
what is wrong with this test ? is my first time doing test on jest.
Related
I am new to unit testing and have a question about the mounted method inside the component.
I am testing if the button text is correctly displaying depends on one of the data values, and it passes. However, I have one method in mounted() inside the component and requests API call which is called from nuxt context.
The method is failing and consoling err message from try and catch because looks like it can not find nuxt context inside the test. This is not affecting my test but I wonder if it is fine, or do I need to fix something.
This is my component.
<template>
<button>
{{variations.length > 0 ? 'Select options' : 'add to cart'}}
</button>
</template>
<script>
data() {
return {
variations: [],
}
},
mounted() {
this.getVaridations()
},
methods: {
async getVaridations() {
try {
const variations = await this.$getVatiation.get() // this part is failing and consoling err message from catch
this.variations = variations
} catch (err) {
console.log(err) // consoling as TypeError: Cannot read properties of undefined (reading 'get')
}
},
},
</script>
This is testing
describe('Product.vue', () => {
it('btn display as "Select options" when there is validation', () => {
const wrapper = mount(Product, {})
expect(wrapper.find('.product-btn').text()).toBe('Select options') // This passes
})
})
You can mock any component methods like
import { shallowMount } from "#vue/test-utils";
describe('Product.vue', () => {
it('btn display as "Select options" when there is validation', () => {
const mocks = {
$getVatiation: {
get: () => [] // returns an empty array, change to what you want to return
}
}
const wrapper = shallowMount (Product, {mocks}) // send your mocks as an argument
expect(wrapper.find('.product-btn').text()).toBe('Select options')
})
})
When I run test, it show TypeError: Cannot destructure property 'travelDatas' of '(0 , _GetTravelDatas.getTravelDatas)(...)' as it is undefined.
As you see the screenshot: unit test
There isn't any console error or warning.
Could anyone help please
travelListTest.spec.js
import { mount, shallowMount } from '#vue/test-utils'
import TravelList from '../../src/components/TravelList.vue'
import { getTravelDatas } from '../../src/composables/GetTravelDatas'
import ElementPlus from 'element-plus'
const wrapper = shallowMount(TravelList, {
global: {
plugins: [ElementPlus]
}
})
jest.mock('../../src/composables/GetTravelDatas')
describe('TravelList Test', () => {
test('click more will call GoToTravelDetailPage', () => {
wrapper.vm.GoToTravelDetailPage = jest.fn()
console.log(wrapper.html())
wrapper.find('.el-button').trigger('click')
expect(wrapper.vm.GoToTravelDetailPage).toHaveBeenCalled()
})
})
TravelList.vue
.....
<script>
import { ref } from '#vue/reactivity';
import { useRouter } from "vue-router";
import { getTravelDatas } from '../composables/GetTravelDatas'
export default {
name: 'TravelList',
setup() {
const { travelDatas } = getTravelDatas();
const router = useRouter();
function GoToTravelDetailPage(acctractionId) {
router.push({ path: `/travelDetail/${acctractionId}` })
}
return { travelDatas, GoToTravelDetailPage };
},
};
</script>
GetTravelDatas.js
import axios from "axios";
import { ref } from '#vue/runtime-core';
export function getTravelDatas() {
const travelDatas = ref([])
axios.get('https://localhost:5001/MyTravel/GetTravelData')
.then((response) => {
if (!response.data.success) {
alert(response.data.errorMessage)
}else{
travelDatas.value = response.data.travelDetail
}
}).catch((error) => {
alert('Unexpected Error: ', error.message)
console.log(error)
});
return { travelDatas }
}
You are mocking the GetTravelDatas module with an auto-mock version by calling:
jest.mock('../../src/composables/GetTravelDatas')
That means that all the methods exported from that module will be mocked (the real code of the method will not be called) and the mocked version will return undefined when called.
In the code you are testing you then have:
const { travelDatas } = getTravelDatas();
Reading the travelDatas property from undefined is causing the error you are seeing.
You should mock the getTravelDatas method so that it returns the appropriate data. For example, returning an empty array would look like:
getTravelDatas.mockReturnValue([]);
I'm testing for the existence of 3 Subcomponents inside a Component. The test passes, but this warning is shown:
(node:52604) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'getters' of undefined
(node:52604) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2)
(node:52604) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
This is the code of the Test:
const localVue = createLocalVue();
localVue.use(TPlugin(url)); //EDIT: ADD PLUGIN
localVue.use(Vuex);
it('WIP Fixing. Check the 3 Sub-Components of ChatWindow', () => {
const onClose = jest.fn();
const onMinimize = jest.fn();
const store = new Vuex.Store({
state: {
title: "My Web Chat",
titleIconUrl: "",
visibility: "minimized",
showCloseButton: false
},
getters: {
title: (state) => state.title,
lastName: (state) => state.lastName,
state: (state) => state.visibility
}
})
const wrapper = shallowMount(ChatWindow, {
propsData: {
onClose,
onMinimize
},
store,
localVue
})
expect(wrapper.findComponent(Header).exists()).toBe(true)
expect(wrapper.findComponent(MessageList).exists()).toBe(true)
expect(wrapper.findComponent(UserInput).exists()).toBe(true)
});
There must be some mistake in the way I mount the component with Vuex, any ideas?
UPDATE, here is an extract from my index.js:
export const store = new Vuex.Store({
state: {
title: "My funky title",
url: DEFAULT_URL,
visibility: "minimized",
//more variables....
},
mutations: {
title(state, newTitle) {
state.title = newTitle
},
url(state, newUrl) {
state.url = newUrl
},
visibility(state, visibility) {
state.visibility = visibility
},
//more variables...
}
getters: {
title: state => state.title,
url: state => state.url,
visibility: visibility => state.visibility,
//more variables.....
state: state => {
return { 'visibility': state.visibility }
},
}
})
window['TWChat'] = {
initialize(element, twcProps) {
Vue.prototype.$store = store;
if (twcProps.title) {
store.commit('title',twcProps.title);
}
///more similar if/commit blocks...
// check required properties
if (!store.getters.url) {
// TODO: thow error if url is missing?
return
}
Vue.use(TPlugin(store.getters.title));
Vue.prototype.$extensionMethods = functionMap;
//TChat is the parent of ChatWindow, the componentent of the Test
above.
new Vue({
render: (h) => h(TChat, { props: { } }),
}).$mount(element);
}
}
index.ejs code:
<div id="t-chat"></div>
<script src="t-chat.js"></script> -->
<script>
window.onload = function () {
var element = document.getElementById('t-chat');
const someProps = {
'url': '<%= process.env.ENDPOINT_URL %>',
'title':'<%= process.env.HEADER_TITLE %>'
}
window.TChat.initialize(element, someProps);
}
</script>
I'm trying to learn a bit more about JS tests.
I've got a basic React component that fetch()'s data when it mounts.
When running the app, the component works as expected and gets the data.
However when testing with Jest, I can see that the call has been made but the promise is always rejected?
I've been following this example to produce the tests below.
Not sure about mocking promises with Jest, any pointers would be a huge help!
Component
import React from 'react';
import './App.scss';
import * as Utils from './Functions';
import Header from './components/Header';
import Loader from './components/Loader';
import Table from './components/Table';
export default class App extends React.Component {
constructor(props) {
super(props);
/*
Initialise state:
# Loading: true
*/
this.state = {
loading: true,
};
}
/*
When component mounts,
# Call function to get data
# Set state with promise response
*/
componentDidMount = () => {
/* Function to grab data
I've created a local express server to get around the cors issue
*/
Utils.initData('http://localhost:8888/mock/all').then(data => {
// Finally set state to reload component with new data
this.setState({
loading: false,
teams: data,
})
})
}
render() {
const { loading, teams } = this.state;
return (
<div id="app">
<Header />
<div className="table">
{loading && (<Loader />)}
{!loading && (<Table data={teams} loading={loading} />)}
</div>
</div>
);
}
}
Functions
export const initData = (dataURL) => {
try {
// Get data using the Fetch API
return fetch(dataURL).then(
response => response.json()
)
// Then sanitize the data
.then(data => sanitizeData(data));
} catch (error) {
console.warn(error);
return error;
}
}
export const sanitizeData = (data) => {
console.log(data)
// Do loads of stuff with the data
}
Test
import React from 'react';
import ReactDOM from 'react-dom';
import { shallow, mount } from 'enzyme';
import App from './App';
import Table from './components/Table';
import Header from './components/Header';
import * as Utils from './Functions';
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
configure({ adapter: new Adapter() });
describe('App', () => {
it('- Renders the header', () => {
const div = document.createElement('div');
ReactDOM.render(<Header />, div);
ReactDOM.unmountComponentAtNode(div);
});
it('- Renders the table', () => {
const div = document.createElement('div');
ReactDOM.render(<Table />, div);
ReactDOM.unmountComponentAtNode(div);
});
it('- Renders the full app', () => {
const div = document.createElement('div');
ReactDOM.render(<App />, div);
ReactDOM.unmountComponentAtNode(div);
});
});
describe('Gets data', () => {
it('fetches data from server when server returns a successful response', () => {
const mockSuccessResponse = {};
const mockJsonPromise = Promise.resolve(mockSuccessResponse);
const mockFetchPromise = Promise.resolve({
json: () => mockJsonPromise,
});
jest.spyOn(global, 'fetch').mockImplementation(() => mockFetchPromise); // 4
const wrapper = shallow(<App />);
expect(global.fetch).toHaveBeenCalledTimes(1);
expect(global.fetch).toHaveBeenCalledWith('http://localhost:8888/mock/all');
});
});
Error messages
I don't get any error in the app itself but while the test runs I get:
(node:4082) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'forEach' of undefined
[1] (node:4082) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
[1] (node:4082) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
The forEach mentioned above is from the sanitizeData() function and is there because the data param is {} when testing...
You are returning {} in mockJsonPromise which gets passed on to sanitizeData() add hence the forEach loop is not working. Return a list with mock data instead.
const mockSuccessResponse = {};
const mockJsonPromise = Promise.resolve(mockSuccessResponse);
const mockFetchPromise = Promise.resolve({
json: () => mockJsonPromise,
});
jest.spyOn(global, 'fetch').mockImplementation(() => mockFetchPromise);
According to the above code response.json() will resolve to mockSuccessResponse which is {}
I am writing tests to test my saga. Can anyone guide me how I can change the code below so that I can mock the api call? I don`t want to test real data.
import { call, put } from 'redux-saga/effects';
import { API_BUTTON_CLICK_SUCCESS, } from './actions/consts';
import { getDataFromAPI } from './api';
it('apiSideEffect - fetches data from API and dispatches a success action', () => {
const generator = apiSideEffect();
expect(generator.next().value)
.toEqual(call(getDataFromAPI));
expect(generator.next().value)
.toEqual(put({ type: API_BUTTON_CLICK_SUCCESS }));
expect(generator.next())
.toEqual({ done: true, value: undefined });
});
The getDataFromAPI()
import axios from "axios";
export const getDataFromAPI =(
method: string,
url: string,
path: string,
data?: any
) =>{
switch (method) {
case "create": {
return axios
.post(url + path, data, {
headers: {
Accept: "application/json",
"content-type": "application/json"
}
})
.catch(error => {
throw error.response;
});
}
I have tried to use
jest.mock('../../src/Utilities/api');
const { callApi } = require('../../src/Utilities/api');
callApi.mockImplementation( () => console.log("some api call"));
I am having the error
TypeError: Cannot read property 'mockImplementation' of undefined
at Object.<anonymous> (src/Payments/PaymentSagas.spec.ts:10:17)
at new Promise (<anonymous>)
at Promise.resolve.then.el (node_modules/p-map/index.js:46:16)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7)
I usually do
import * as apis from '../../src/Utilities/api';
jest.spyOn(api, "callApi");
api.callApi.mockImplementation(/* your mock */);
easily exportable as a per-se function
export function spyUtil(obj, name, mockFunction = undefined) {
const spy = jest.spyOn(obj, name);
let mock;
if (mockFunction) {
mock = jest.fn(mockFunction);
obj[name].mockImplementation(mock);
}
return { spy, mock };
}
and consumable, in your test
spyUtil(apis, "callApi", jest.fn())