Testing simple GET request using Axios and Jest - unit-testing

I'm using axios 0.18.0, jest 22.4.2 and I'm trying to test a simple request using Axios and Jest based on the Axios documentation example and Jest Async / Await documentation
// services.js
import axios from 'axios';
export const getUser = () => axios.get('https://api.github.com/users/mzabriskie');
// services.spec.js
import { getUser } from './services';
it('should return data from github user', async () => {
jasmine.DEFAULT_TIMEOUT_INTERVAL = 30000;
const result = await getUser();
console.log('RESULT -->', result);
});
Once the DEFAULT_TIMEOUT_INTERVAL is passed (in this example 30 seconds), the test gives the following error message:
Error: Timeout - Async callback was not invoked within timeout
specified by jasmine.DEFAULT_TIMEOUT_INTERVAL
And it never reaches the console.log statement.
Any idea what I might be missing here or how to debug this?

So it turns out that the problem was using the dependency jest-mock-axios which it seems once it's configured it never allows HTTP requests to go through in the tests, even if you are not using it in a specific test (like in the original question example).
So in order to fix this all related requests tests must have defined mocks in order to work (or else remove the jest-mock-axios dependency entirely if you wish to test real API requests).

Related

How to get code coverage metrics from integration tests using serverless-offline and Supertest?

I'm building an AWS Lambda function and trying to write some integration tests for it. The Lambda function is running locally using serverless-offline plugin and simply receive a GET request with some query parameters. I'm using Jest and Supertest to write my integration tests as follow:
import request from 'supertest';
describe('User position handler', () => {
it('should return history', () => {
const server = request('http://0.0.0.0:4000');
return server
.get(`/local/position?type=month&period=3`)
.expect(200)
.expect((response) => {
console.log('RESPONSE', response);
expect(response.body).toHaveLength(3);
});
});
});
The problem is that when I run Jest with collect coverage option the code reached by the request sent with Supertest is not computed in the metrics. Running jest --collectCoverage the result is:
The question is that I know that, for example, infra/handlers/user-position.ts is being reached and covered more than 0% statements, but the coverage metrics don't show as expected. Also, I know that user-monthly-position.service.impl.ts is being reached at some point of the flow since this service is responsible for returning data from an external service and the response from Supertest is returning data. The green lines are from files covered by unit test that are using only Jest (and not Supertest, obviously)
I know that when using Supertest with Express framework I can pass an instance of the Express app. to the request function. This way I think that Jest can "inspect" or "instrument" the function call stack to measure the coverage (code sample below). But how can I do the same passing the URL of a running serverless-offline Lambda?
const request = require('supertest');
const assert = require('assert');
const express = require('express');
const app = express();
app.get('/user', function(req, res) {
res.status(200).json({ name: 'john' });
});
request(app)
.get('/user')
.expect('Content-Type', /json/)
.expect('Content-Length', '15')
.expect(200)
.end(function(err, res) {
if (err) throw err;
});
Here the code of my handler function:
export default async (event: APIGatewayEvent): Promise<APIGatewayProxyResult> => {
await cacheService.bootstrapCache();
const userMonthlyPositionService = new UserMonthlyPositionServiceImpl(
cacheService.connectionPool,
);
const getUserMonthlyPositionHistory = new GetUserMonthlyPositionHistory(
userMonthlyPositionService,
);
const result = await getUserMonthlyPositionHistory.execute({
cblc: 999999,
period: 3,
type: 'month',
});
return buildResponse(200, result);
};
My question is: how can I collect right code coverage metrics from Jest using Supertest and Serverless Framework? Am I forgetting a detail? Thanks!

Stenciljs unit testing getting error : ReferenceError XMLHttpRequest is not defined

I'm trying to create units test for my stencil js component, in the compnentWillLoad() method it will do an HTTP request (using rxjs).when I'm run the test getting error ReferenceError: XMLHttpRequest is not defined.But when removing the HTTP request from the componentWillLoad() method test passed.
My test as below,
it('should render my component', async () => {
const page = await newSpecPage({
components: [MyComponent],
html: `<my-component></my-component>`,
});
expect(page.root).toEqualHtml(`<my-component></my-component>`);
});
I'm getting error ReferenceError: XMLHttpRequest is not defined
XMLHttpRequest is indeed not defined in the virtual DOM context that is created when you use newSpecPage.
The best solution for you is probably to write this as an E2E test instead, using newE2EPage, which is more suited for complete end-to-end testing because it runs in a real browser context where XMLHttpRequest will be available.
it('should render', async () => {
const page = await newE2EPage({ html: '<my-component></my-component>' });
const myComponent = page.find('my-component');
expect(myComponent).toHaveClass('hydrated');
});
"Spec Page" testing is rather meant for unit testing components that work stand-alone. If your goal is to actually unit-test your component and you just want to be able to instantiate your component but you don't actually need the request to succeed for testing, then you can also use the Build context from Stencil:
import { Build, ... } from '#stencil/core';
export class MyComponent {
componentWillLoad() {
if (!Build.isTesting) {
// make the request
}
}
// ...
}
I had similar troubles with Stencil, Jest and XMLHttpRequest.
First, make sure you call
new window.XMLHttpRequest()
instead of simply calling
new XMLHttpRequest()
This seems to be neccessary when using jsdom and may already resolve your issue.
It didn't resolve mine though, since I wanted to make sure there are no real API calls going on. So I tried to mock XMLHttpRequest. However, I ran into other issues while building the mock and finally decided to refactor my code to use Fetch API instead of XMLHttpRequest which seems to be better supported by Stencil.
You can easily mock fetch using jest
export function mockFetch(status, body, statusText?) {
// #ts-ignore
global.fetch = jest.fn(() =>
Promise.resolve({
status: status,
statusText: statusText,
text: () => Promise.resolve(JSON.stringify(body)),
json: () => Promise.resolve(body),
})
)
}

How to unit test Vue component that uses Axios (or other async update)?

I have a Vue component/view that performs an API request using Axios and updates the component data with the response. I'm using Moxios to mock the Axios request in unit tests.
I tried using Vue.nextTick to postpone assertion of the updated data, but the component has not updated at that point yet. If I add a delay, the assertion works correctly:
setTimeout(() => {
expect(wrapper.text()).toMatch('Updated text')
done()
}, 500)
However this is bad practice, slows down the tests and is a race condition.
Is there some kind of assertion check that would be called every time a component updates? Essentially, I'm looking for something like:
Vue.eventually(() => {
expect(wrapper.text()).toMatch('Updated text')
done()
})
A generic (non-Vue-specific) workaround:
const test = () => {
try {
expect(wrapper.text()).toMatch('Updated text')
done()
} catch (e) {
setTimeout(test, 1)
}
}
setTimeout(test, 1)
However, any failures from the expect are ignored and the test times out without any message if failing.

Unit testing NestJS controller with request

My Controller function definition looks like that:
async login(#Req() request, #Body() loginDto: LoginDto): Promise<any> {
How I could prepare/mockup Request to provide first argument of function from Jest test?
Inside funciton I am setting headers using request.res.set. Should I somehow pass real Request object to function and then check if header is set or rather mockup whole Request object and check if set function was called?
I managed to do that mocking requests and response using node-mocks-http library.
const req = mocks.createRequest()
req.res = mocks.createResponse()
and then passing this as an argument.
const data = await authController.login(req, loginDto)
expect(req.res.get('AccessToken')).toBe(token.accessToken)
I followed a different approach and instead of using node-mocks-http library I used #golevelup/ts-jest, also, instead of testing if the function returns some value, like res.json() or res.status(), I checked if the function was called with the value I wanted to.
I borrowed this approach from Kent C. Dodds's testing workshop, take a look for similar ideas. Anyway, this is what I did in order to mock the Response dependency of my Controller's route:
// cars.controller.spec.ts
import { createMock } from '#golevelup/ts-jest';
const mockResponseObject = () => {
return createMock<Response>({
json: jest.fn().mockReturnThis(),
status: jest.fn().mockReturnThis(),
});
};
... ommited for brevity
it('should return an array of Cars', async () => {
const response = mockResponseObject();
jest
.spyOn(carsService, 'findAll')
.mockImplementation(jest.fn().mockResolvedValueOnce(mockedCarsList));
await carsController.getCars(response);
expect(response.json).toHaveBeenCalledTimes(1);
expect(response.json).toHaveBeenCalledWith({ cars: mockedCarsList });
expect(response.status).toHaveBeenCalledTimes(1);
expect(response.status).toHaveBeenCalledWith(200);
});
And that's it, I think that the implementation details aren't that important but in any case I'll leave the link to the Github repo where you can find the whole project.

How to unit test API calls with mocked fetch() in react-native with Jest

In React Native I use fetch to perform network requests, however fetch is not an explicitly required module, so it is seemingly impossible to mock in Jest.
Even trying to call a method which uses fetch in a test will result in:
ReferenceError: fetch is not defined
Is there a way to test such API requests in react native with Jest?
Inside your test case you can mock any function you want by using Jest's mocks:
fetch = jest.fn(() => Promise.resolve());
This approach works only for the promise-based test cases (see pit in the Jest docs).
As far as fetch is an async function, you need to run all your tests using pit (read more about async tests here).
Another approach where you mock the global fetch object:
const mockSuccesfulResponse = (
status = 200,
method = RequestType.GET,
returnBody?: object
) => {
global.fetch = jest.fn().mockImplementationOnce(() => {
return new Promise((resolve, reject) => {
resolve({
ok: true,
status,
json: () => {
return returnBody ? returnBody : {};
},
});
});
});
};
The above helper method can be modified any way you want :-) Hope it helps someone
Rather than rolling your own mock, you can use the jest-fetch-mock npm package to override the global fetch object. That package allows you to set up fake responses and verify sent requests. See that link for extensive usage examples.
I solved this by adding isomorphic-fetch.
$ npm install --save isomorphic-fetch
and using it like
import fetch from 'isomorphic-fetch';
...
fetch('http://foo.com');
whatwg-fetch might work as well
Suppose you want to test resolve and reject cases, for this first you mock the fetch behaviour and then use Jest's rejects and resolves methods with with assertion block
function fetchTodos() {
return fetch(`${window.location.origin}/todos.json`)
.then(response => response.json())
.catch(error => console.log(error))
}
describe('fetchTodos', () => {
it('returns promise resolving to parsed response', () => {
global.fetch = jest.fn(() => Promise.resolve({ json: () => ''}))
expect(fetchTodos()).resolves.toBe('');
})
it('returns promise handling the error', async () => {
global.fetch = jest.fn(() => Promise.reject(''))
expect(fetchTodos()).rejects.toBe('')
})
})
As #ArthurDenture recommended, you can use fetch-mock, but there are some additional packages you will need to install to make it work with React Native and Jest:
$ npm install --save-dev fetch-mock
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save-dev babel-preset-env
You can then mock fetch requests in your tests. Here is an example:
// __tests__/App.test.js
import React from 'react';
import App from '../App';
import fetchMock from 'fetch-mock';
import renderer from 'react-test-renderer';
it('renders without crashing', () => {
fetchMock.mock('*', 'Hello World!');
const rendered = renderer.create(<App />).toJSON();
expect(rendered).toBeTruthy();
});
Due to problems using fetch-mock with jest, I've release fetch-mock-jest. It basically gives the full fetch-mock api, but with a few jest-specific helpers, and works out of the box with jest, without needing to do any tricky wiring yourself
As shown in the react-testing-library documentation, you can use the jest.spyOn() function, which will mock the fetch function only for the next time it is called.
const fakeUserResponse = {token: 'fake_user_token'}
jest.spyOn(window, 'fetch').mockImplementationOnce(() => {
return Promise.resolve({
json: () => Promise.resolve(fakeUserResponse),
})
})
react-testing-library