How to test catch from function like this:
getApi () {
const URL = '/api/division?key='
axios.get(URL)
.then((response) => {
this.counter = response.data
})
.catch(err => {
alert(err)
})
}
I'm using axios and vue js with testing JEST. Hope any solution, thanks :')
Try axios-mock-adapter, which can mock the results of axios.get() calls, allowing you to simulate a network error/timeout for a specific request (thus invoking the catch callback in your code):
import axios from "axios";
import MockAdapter from "axios-mock-adapter";
const mock = new MockAdapter(axios);
mock.onGet(`/* URL used by component */`).networkError();
Example unit tests for getApi():
it("does not modify username from network error", async () => {
mock.onGet(`/* URL used by component */`).networkError();
await wrapper.vm.getApi();
expect(wrapper.vm.username).toBe(INIT_USERNAME);
});
it("does not modify username from network timeout", async () => {
mock.onGet(`/* URL used by component */`).timeout();
await wrapper.vm.getApi();
expect(wrapper.vm.username).toBe(INIT_USERNAME);
});
demo
Related
I am using an axios interceptor to add authorization token to its header. The interceptor works fine.
//api.js
import { getAccessToken } from "./utils";
const apiInstance = axios.create();
apiInstance.interceptors.request.use((configIns) => {
const token = getAccessToken();
configIns.headers.Authorization = token ? `Bearer ${token}` : "";
return configIns;
});
export { apiInstance };
Here is my test file to test the interceptor.
// api.test.js
import { apiInstance } from "./api"
import {getAccessToken} from "./utils";
describe("request interceptor", () => {
it("API request should add authorization token to header", () => {
const getAccessToken = jest.fn(getAccessToken);
getAccessTokenMock.mockReturnValue("token");
const result = apiInstance.interceptors.request.handlers[0].fulfilled({ headers: {} });
expect(getAccessTokenMock.mock.calls.length).toBe(1);
expect(result.headers).toHaveProperty("Authorization");
});
});
However the getAccessToken function in not getting mocked for some reason inside the interceptor.
The test fails.
That's not how mocks in Jest work. By calling (I assume variable should be getAccessTokenMock, not getAccessToken):
const getAccessTokenMock = jest.fn(getAccessToken);
getAccessTokenMock.mockReturnValue("token");
What you do is: you create new local mock, that when called, would call your getAccessToken function. Then you mock return value. However, your getAccessTokenMock is never called, because it's not the same instance as in your implementation!
What you need to do is mock your actual function getAccessToken from your ./utils file. It can be done, for example, like this:
import { apiInstance } from "./api"
import {getAccessToken} from "./utils";
// Mock here:
jest.mock('./utils', () => ({
getAccessToken: jest.fn(() => 'token')
});
describe("request interceptor", () => {
it("API request should add authorization token to header", () => {
const result = apiInstance.interceptors.request.handlers[0].fulfilled({ headers: {} });
expect(getAccessToken.mock.calls.length).toBe(1);
expect(result.headers).toHaveProperty("Authorization");
});
});
What is happening here is that when file is loaded, jest.mock would run first and would replace your implementation of getAccessToken with the mock - it would be then accessible from both implementation and test (as the same instance), meaning that you can verify if it has been called.
Please find more about mocks here or here. Alternatively you can also use spyOn to achieve similar result (then you don't need jest.mock, but you have to import your getAccessToken function without destructuring).
I am trying to build an app in react native that is suppose to take take two inputs by a user and then make a query to an api and get information about the two inputs. I have been having trouble with redux and redux-thunk and specifically with async actions.
This is the code in my app that i am specifically having trouble with
export const fetchData = url => {
console.log("start Fetching");
return async dispatch => { // this is where the problem is
dispatch(fetchingRequest());
try {
const response = await fetch("https://randomuser.me/api/?results=10");
const json = await response.text();
if (response.ok) {
dispatch(fetchingSuccess(json));
console.log("JSON", json);
} else {
console.log("fetch did not resolve");
}
} catch (error) {
dispatch(fetchingFailure(error));
}
};
console.log("Fetched data");
};
Upon debugging the function, I have ended with finding that when the fetchData function is called the function will execute but the async dispatch that is being returned has undefined behavior.
The output in the debugger when the function is called should be
start Fetching
JSON file information/Error
but the output in the debugger is actually
start Fetching
This is the function in which fetchData is called in
_onPress = () => {
let url = "https://randomuser.me/api/?results=10";
fetchData(url);
console.log("should have fetched");
};
this is the mapDispatchToProps function that I have added. The problem is i do not know what to add inside the function.
const mapStatetoDispatch = (url, dispatch) => {
return {dispatch(fetchData(url))}; // do not know what to place in body of function
};
i have connected it in the component with
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);
these are the action creators that I import, if needed
import {
fetchingSuccess,
fetchingRequest,
fetchingFailure,
fetchData
} from "../data/redux/actions/appActions.js";
Assuming you have added redux-thunk as a middleware, it looks like the errors are here:
_onPress = () => {
const { fetchData } = this.props;
let url = "https://randomuser.me/api/?results=10";
fetchData(url);
console.log("should have fetched");
};
and
const mapStatetoDispatch = dispatch => ({
fetchData: url => dispatch(fetchData(url)),
}};
I am trying to mock an api call using Jest and Vue but I get the error "Expected mock function to have been called with: ... but not called"
I have tried to find a solution but haven't found anything yet.
import DocumentService from "../../src/services/document";
import mockedData from "../__mockData__/documents";
import axios from "axios";
it("should call Document Service and download a document", () => {
let catchFn = jest.fn(),
thenFn = jest.fn();
DocumentService.downloadDocumentById(jwtToken, DocumentURL, id)
.then(thenFn)
.then(catchFn);
// expect(axios.get).toHaveBeenCalledWith(DocumentURL + "/" + id + "/content", {
// headers: { Authorization: "Bearer " + jwtToken, "X-role": "SuperUser" }
// });
expect(axios.get).toHaveBeenCalledWith(DocumentURL);
let responseObj = { data: mockedData };
axios.get.Mockresponse(responseObj);
expect(thenFn).toHaveBeenCalledWith(mockedData);
expect(catchFn).not.toHaveBeenCalled();
});
The test runs synchronously and the expect runs and fails before the Promise callbacks have a chance to run.
Make sure you await the Promise returned by DocumentService.downloadDocumentById to give the callbacks a chance to run:
it("should call Document Service and download a document", async () => { // use an async test function
let catchFn = jest.fn(),
thenFn = jest.fn();
const promise = DocumentService.downloadDocumentById(jwtToken, DocumentURL, id)
.then(thenFn)
.then(catchFn); // remember the Promise
expect(axios.get).toHaveBeenCalledWith(DocumentURL);
let responseObj = { data: mockedData };
axios.get.Mockresponse(responseObj);
await promise; // wait for the Promise
expect(thenFn).toHaveBeenCalledWith(mockedData); // SUCCESS
expect(catchFn).not.toHaveBeenCalled(); // SUCCESS
});
Had the same trouble, made it this way:
import axios from 'axios';
in test axios.get = jest.fn();
expect( axios.get ).toBeCalledWith( yourUrl );
I created a new vue project using the command vue create axe using vue-cli-3.0.016beta. Then installed axios using npm install axios --save. In the main.js file I imported axios as shown below.
import Vue from 'vue'
import App from './App.vue'
import axios from 'axios'
Vue.config.productionTip = false
Vue.use(axios)
new Vue({
render: h => h(App)
}).$mount('#app')
There is not a bit of code change other than this. Still I get an error like the following:
Unhandled promise rejection
TypeError
columnNumber: 7
fileName: "http://localhost:8080/app.js line 1065 > eval"
lineNumber: 57
message: "parsed is undefined"
stack: "isURLSameOrigin#webpack-internal:///./node_modules/axios/lib/helpers/isURLSameOrigin.js:57:7\ndispatchXhrRequest#webpack-internal:///./node_modules/axios/lib/adapters/xhr.js:109:50\nPromise#webpack-internal:///./node_modules/core-js/modules/es6.promise.js:177:7\nxhrAdapter#webpack-internal:///./node_modules/axios/lib/adapters/xhr.js:12:10\ndispatchRequest#webpack-internal:///./node_modules/axios/lib/core/dispatchRequest.js:59:10\nrun#webpack-internal:///./node_modules/core-js/modules/es6.promise.js:75:22\nnotify/<#webpack-internal:///./node_modules/core-js/modules/es6.promise.js:92:30\nflush#webpack-internal:///./node_modules/core-js/modules/_microtask.js:18:9\n"
__proto__: Object { stack: "", … }
I want to axios globally to use interceptors, hence calling it here in main.js. But if I use it in a view-page there is no error!
is this a bug or I'm doing it wrong? Kindly help me to fix this and use axios globally.
Thanks
so the error I see is here
Vue.use(axios)
Vue.use expects a vue installable plugin.
You could have a look at vue-axios
import axios from 'axios'
import VueAxios from 'vue-axios'
Vue.use(VueAxios, axios)
but I would highly discourage it.
It's best to create your own ApiHandler.js file that handles all the remote stuff separately, and you can easily call from anywhere including vue components and vuex.
here is the beginning of my class
<script>
import axios from 'axios';
class ApiHandler{
constructor(apiUrl) {
this.axios = axios;
this.apiUrl = apiUrl || ''; // this line allow passing a custom endpoint for testing
this.config = {
headers: { 'Cache-Control': 'no-cache' }, // can setup to prevent all caching
baseURL: this.apiUrl,
};
}
/**
* #param {Object} payload
* #param {String} payload.username
* #param {String} payload.password
*/
login({ username, password }) {
return new Promise((resolve, reject) => {
this.axios.post('/api/login', { username: username.toLowerCase(), password }, this.config)
.then((response) => {
if (response.code === 200 && response.body && response.body.token) {
resolve(response.body.token);
} else {
reject('Bad Login');
}
})
.catch((err) => {
reject('internal error');
});
});
}
}
</script>
you can then call this from anywhere by...
<script>
import ApiHandler from '../lib/ApiHandler';
const apiRequest = new ApiRequest();
// and then anywhere in the script
let payload = {
username:'someuser',
password:'somepassword',
};
apiRequest.login(payload)
.then(()=>{
// yay - I'm logged in
})
.catch(err => {
// oh oh, display error
})
</script>
this gives you much more flexibility and allows you to separate the remote actions and allows doing first-leg response handling separate of your component, which allows more re-usability.
instead of
Vue.use(axios);
you should
Vue.prototype.$axios = axios;
then you can use it globally
login() {
this.$axios.post('<host>/api/login', data)
.then((res) => { // dosomething })
.catch((err) => { // dosomething });
}
if you want to add globally interceptors with axios, you can
// Add a request interceptor
axios.interceptors.request.use(function (config) {
// Do something before request is sent
return config;
}, function (error) {
// Do something with request error
return Promise.reject(error);
});
// Add a response interceptor
axios.interceptors.response.use(function (response) {
// Do something with response data
return response;
}, function (error) {
// Do something with response error
return Promise.reject(error);
});
// and
Vue.prototype.$axios = axios;
Good day. I have the following problem:
I have an item editor.
How it works: I push 'Add' button, fill some information, click 'Save' button.
_onSaveClicked function in my react component handles click event and call function from service, which sends params from edit form to server and return promise.
_onSaveClicked implements
.then(response => {
console.log('I\'m in then() block.');
console.log('response', response.data);
})
function and waits for promise result. It works in real situation.
I created fake service and placed it instead of real service.
Service's function contains:
return Promise.resolve({data: 'test response'});
As you can see fake service return resolved promise and .then() block should work immediatly. But .then() block never works.
Jest test:
jest.autoMockOff();
const React = require('react');
const ReactDOM = require('react-dom');
const TestUtils = require('react-addons-test-utils');
const expect = require('expect');
const TestService = require('./service/TestService ').default;
let testService = new TestService ();
describe('TestComponent', () => {
it('correct test component', () => {
//... some initial code here
let saveButton = TestUtils.findRenderedDOMComponentWithClass(editForm, 'btn-primary');
TestUtils.Simulate.click(saveButton);
// here I should see response in my console, but I don't
});
});
React component save function:
_onSaveClicked = (data) => {
this.context.testService.saveData(data)
.then(response => {
console.log('I\'m in then() block.');
console.log('response', response.data);
});
};
Service:
export default class TestService {
saveData = (data) => {
console.log('I\'m in services saveData function');
return Promise.resolve({data: data});
};
}
I see only "I'm in services saveData function" in my console.
How to make it works? I need to immitate server response.
Thank you for your time.
You can wrap your testing component in another one like:
class ContextInitContainer extends React.Component {
static childContextTypes = {
testService: React.PropTypes.object
};
getChildContext = () => {
return {
testService: {
saveData: (data) => {
return {
then: function(callback) {
return callback({
// here should be your response body object
})
}
}
}
}
};
};
render() {
return this.props.children;
}
}
then:
<ContextInitContainer>
<YourTestingComponent />
</ContextInitContainer>
So your promise will be executed immediately.