I have this very simple test for a GET, run via Mocha
import chai from 'chai'
import chaiHttp from 'chai-http'
import {should, expect} from 'chai'
import nock from 'nock'
import request from 'supertest'
const url = 'http://mysite-beta.com'
const api = '/api/user'
describe('/GET route', () => {
it('it should GET the mocked response', (done) => {
nock(url)
.get(api)
.reply(200, {
"status": 200,
"message": "This is a mocked response"
});
request(url)
.get(api)
.end(function (err, res) {
// ****** the next line gives an error ******
expect(res.body.status).to.equal(200);
expect(res.body.message).to.equal("This is a mocked response");
done();
});
});
});
But when I run it I get TypeError: Cannot read property 'body' of undefined. res is undefined
Everything is setup e.g. npm packages etc
This is a good example using nock with mocja and supertest:
var nock = require('nock');
var request = require('supertest')("http://api.postcodes.io");
var expect = require('chai').expect;
describe("Testing API with a mocked backend", function () {
it("returns a successful mocked response", function (done) {
//specify the url to be intercepted
nock("http://api.postcodes.io")
//define the method to be intercepted
.get('/postcodes/')
//respond with a OK and the specified JSON response
.reply(200, {
"status": 200,
"message": "This is a mocked response"
});
//perform the request to the api which will now be intercepted by nock
request
.get('/postcodes/')
.end(function (err, res) {
//assert that the mocked response is returned
expect(res.body.status).to.equal(200);
expect(res.body.message).to.equal("This is a mocked response");
done();
});
})
});
Related
I am calling an API using fetch and writing test cases for that. While making the Fetch call, I am expected mocked data but getting API error message.
Please help me to know why its not mocking the data.
Using Jest, jest-fetch-mock modules. Code is as follow
const login = (username, password) => {
return fetch('endpoint/login', () => {
method: 'POST',
body: JSON.stringify({
data :{
username,
password
}
})
})
.then (res => res.json())
.then (data => {
if(data.response){
return {
response: "accessToken",
error: null
}
}else{
return {
response: null,
error: "Something went wrong"
}
}
})
}
Now I am writing Unit Test to test this api, as below :-
test("Auth Success Scenario", async () => {
const onResponse = jest.fn();
const onError = jest.fn();
fetchMock.mockResponseONce(JSON.stringify({
data: {
response: "accessToken",
error: null
}
}));
return await AuthService.login("andy","password")
.then(onResponse)
.catch(onError)
.finally( () => {
console.log(onResponse.mock.calls[0][0]) // its return API error not mocked data
})
})
It was meant to be comment, sadly I don't have enough reputation.
Have you enabled jest mocks as specified in the documentation link
Create a setupJest file to setup the mock or add this to an existing setupFile. :
To setup for all tests
require('jest-fetch-mock').enableMocks()
Add the setupFile to your jest config in package.json:
"jest": {
"automock": false,
"setupFiles": [
"./setupJest.js"
]
}
Because that seems to be the only case, in which fetch will try to make actual call to the API, instead of giving mocked response, thus causing failure.
You can even enable mocks for specific test file as well
import fetchMock from "jest-fetch-mock";
require('jest-fetch-mock').enableMocks();
When running a test using jest I have the basic test suit syntax:
jest.mock('axios');
describe('app', () => {
let render
beforeEach(() => {
axiosMock.get.mockResolvedValueOnce({
data: {greeting: 'hello there'},
}),
render= renderApp()
});
test('should render something', () => {
expect(something).toBeInTheDocument();
});
});
The problem is I have interceptors in my code which when running the test with jest command outputs:
TypeError: Cannot read property 'interceptors' of undefined
and points to the interceptors object
axiosInstance.interceptors.request.use(...
axiosInstance is the a variable storing the return of axios.create
export const axiosInstance = axios.create({...
Refered to this axios thread on SO How do I test axios in jest but it doesn't involve any interceptors so didn't really help.
This was enough in the end, plain and simple jest.fn()
jest.mock('axios', () => {
return {
interceptors: {
request: { use: jest.fn(), eject: jest.fn() },
response: { use: jest.fn(), eject: jest.fn() },
},
};
});
Make sure to mock out the interceptors and axios.create if used:
// Replace any instances with the mocked instance (a new mock could be used here instead):
axios.create.mockImplementation((config) => axios);
// Mock out the interceptor (assuming there is only one):
let requestCallback = () => {
console.log("There were no interceptors");
};
axios.interceptors.request.use.mockImplementation((callback) => {
requestCallback = callback;
});
// Mock out the get request so that it returns the mocked data but also calls the
// interceptor code:
axios.get.mockImplementation(() => {
requestCallback();
return {
data: "this is some data"
};
});
Note if this doesn't work:
This example assumes that the create and interceptor calls are in a place where Jest can mock them out. Placing the axios.create or axiosInstance.interceptors.request.use lines outside the scope of the function may cause the above mocking to fail. This is an example file where Jest can mock them out:
const axios = require('axios');
const DEBUG = true;
const customRequest = (url) => {
// Example of axios.create from https://www.npmjs.com/package/axios#axioscreateconfig
const axiosInstance = axios.create({
baseURL: 'https://some-domain.com/api/',
timeout: 1000,
headers: {'X-Custom-Header': 'foobar'}
});
// Example of interceptor taken from https://stackoverflow.com/a/52737325/7470360:
axiosInstance.interceptors.request.use((config) => {
if (DEBUG) { console.info("Request called", config); }
return config;
}, (error) => {
if (DEBUG) { console.error("Request error ", error); }
return Promise.reject(error);
});
return axiosInstance.get(url);
}
module.exports = customRequest;
The mocking code will mock out the axios.create call and the axiosInstance calls in customRequest. Moving either the creation or interception outside the function will cause the mocks to fail.
Here is how I mocked axios.create and its interceptors:
jest.mock('axios', () => {
return {
create: () => {
return {
interceptors: {
request: {eject: jest.fn(), use: jest.fn()},
response: {eject: jest.fn(), use: jest.fn()},
},
};
},
};
});
Afterwards I was able to call the following in my test code:
const client = axios.create({
baseURL: 'http://some-url.com',
});
client.interceptors.request.use(config => {
// some other test code
return config;
});
I can mock a get request with:
mockAxios.get.mockImplementationOnce(() => Promise.resolve({
data: { mockResponse },
}));
But how can I mock a Post request?
I use MockAdapter from axios-mock-adapter as follows:
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import requestGenerator, { API } from './httpClient';
// This sets the mock adapter on the default instance
const mock = new MockAdapter(API);
describe('Test for api calls', () => {
it('Should test for postReq method for Location not reachable by car', async () => {
const mockDirectionResponse = {
status: 'failure',
error: 'Location not accessible by car'
};
mock.onPost('/route/token').reply(200, mockDirectionResponse);
const response = await requestGenerator.postReq('/route/token');
expect(response.data).toEqual(mockDirectionResponse);
});
});
I'm using axios in my react app using import axios from 'axios in many of my scripts. I want to use sort of a middleware that is invoked for all axios calls/errors. How do I approach this?
As per the documentation - You need to create a file i.e
// api-client.js
import axios from 'axios';
// Add a request interceptor
axios.interceptors.request.use(function (config) {
// Do something before request is sent
console.log(config);
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);
});
export default axios;
Then from your container or controller, import above file:
// Home.js
import apiClient from './api-client.js';
Interceptors are the Axios way of doing this. For me though, it was too limited, tangled in Axios' API, difficult to test, etc.
Axios-middleware
So I wrote the axios-middleware module, a simple middleware service that hooks itself in your axios instance (either global or a local one) and provides a simple, self-contained and easily testable middleware API.
Note: it shines in bigger apps where minimal coupling is really important.
Simple example
Here's a simple example from the documentation
import axios from 'axios';
import { Service } from 'axios-middleware';
const service = new Service(axios);
service.register({
onRequest(config) {
console.log('onRequest');
return config;
},
onSync(promise) {
console.log('onSync');
return promise;
},
onResponse(response) {
console.log('onResponse');
return response;
}
});
console.log('Ready to fetch.');
// Just use axios like you would normally.
axios('https://jsonplaceholder.typicode.com/posts/1')
.then(({ data }) => console.log('Received:', data));
It should output:
Ready to fetch.
onRequest
onSync
onResponse
Received: {userId: 1, id: 1, title: ...
Testing a middleware
Say we have the following self-contained middleware class that we want to test.
export default class ApiErrorMiddleware {
constructor(toast) {
this.toast = toast;
}
onResponseError(err = {}) {
let errorKey = 'errors.default';
const { response } = err;
if (response && response.status) {
errorKey = `errors.${response.status}`;
} else if (err.message === 'Network Error') {
errorKey = 'errors.network-error';
}
this.toast.error(errorKey);
throw err;
}
}
Then it's really easy, we don't even need to mock Axios.
import ApiErrorMiddleware from '#/middlewares/ApiErrorMiddleware';
describe('ApiErrorMiddleware', () => {
let toast;
let middleware;
// Jest needs a function when we're expecting an error to be thrown.
function onResponseError(err) {
return () => middleware.onResponseError(err);
}
beforeEach(() => {
toast = { error: jest.fn() };
middleware = new ApiErrorMiddleware(toast);
});
it('sends a code error message', () => {
expect(onResponseError({ response: { status: 404 } })).toThrow();
expect(toast.error).toHaveBeenLastCalledWith('errors.404');
});
});
I have a function like so in a react component. How do I test a successful request and an unsuccessful request?
deleteQuestion(id) {
axios.delete('/api/questions/' + id)
.then(response => {
this.setState({message: "Deletion Successful!"});
}).catch(error => {
var errorMessage = 'Question not deleted: ' + error.response.data.message;
this.setState({error: errorMessage});
});
}
And I was thinking of doing something like this for the testing but this obviously does not work. Basically, the console.log and assertion in the final function do not run.
import React from 'react';
import { mount, shallow } from 'enzyme';
import axios from 'axios';
import axios from 'axios';
import QuestionList from './QuestionList';
import sinon from 'sinon';
beforeEach(function () {
// import and pass your custom axios instance to this method
moxios.install()
})
afterEach(function () {
// import and pass your custom axios instance to this method
moxios.uninstall()
})
it('should modals and <table>', () => {
const wrapper = shallow(<QuestionList/>);
wrapper.instance().deleteQuestion()
moxios.wait(function () {
let request = moxios.requests.mostRecent()
request.respondWith({
status: 200,
response: [
{ id: 1, question: 'Fred', answer: 'Flintstone' },
{ id: 2, question: 'Wilma', answer: 'Flintstone' }
]
}).then(function () {
console.log('hello')
expect(wrapper.state().message).to.equal('Deletion Successful');
done()
})
})
});
You can use some mock http server library. For example https://www.npmjs.com/package/mock-http-server