While testing error responses, the test fails with the expected error (React/Jest/ReactQuery/Axios/MSW) - axios

I am trying to test error states of the following MSW rest endpoint:
import { rest } from 'msw'
export const exceptionHandlers = [
rest.post(config.accountApiUrl + '/login', (req, res, ctx) => {
return res(
ctx.status(500),
ctx.json({ data: { message: 'Mock Error Message' } })
)
})
]
This endpoint is called in a custom hook return function thats using React Query's mutateAsync:
const { mutateAsync } = useMutation(AuthApi.login)
const handleLogin = async (props): Promise<void> => {
await mutateAsync(props, {
onSuccess: async () => {
// this block tests fine
}
onError: async () => {
console.log('!!!')
// it reaches this block, '!!!' is logged to the console,
// but the test still fails with `Request failed with status code 500`
}
})
}
return handleLogin
In a test file:
it('handles network errors', async () => {
mswServer.use(...exceptionHandlers)
const user = userEvent.setup()
const screen = render(<LoginForm />)
const submitButton = screen.getByTestId('Login.Submit')
// Complete form
await user.click(submitButton)
})
It doesnt matter what comes after that, the test always fails with
Request failed with status code 500
at createError (node_modules/axios/lib/core/createError.js:16:15)
at settle (node_modules/axios/lib/core/settle.js:17:12)
at XMLHttpRequestOverride.onloadend (node_modules/axios/lib/adapters/xhr.js:54:7)
at XMLHttpRequestOverride.trigger (node_modules/#mswjs/interceptors/src/interceptors/XMLHttpRequest/XMLHttpRequestOverride.ts:176:17)
at node_modules/#mswjs/interceptors/src/interceptors/XMLHttpRequest/XMLHttpRequestOverride.ts:354:16
But its supposed to fail with status 500. That's the whole point. If I change the handler to return another error, ie ctx.status(404), then the test just fails with that error code.
I've tried wrapping the assertion in a try/catch block but the same thing results. I see examples online of people doing (apparently) exactly this and it works fine, so I'm quite confused what's causing this. All other tests that check success states work as expected.

i've had the same problem.
As far as i could understand, the problem is that in test environment there is no handler for the rejected promise.
https://github.com/TanStack/query/issues/4109

Related

vitest + msw / testing reactQuery (axios) hooks using renderHook / unable to avoid error logging from test suite

Using a custom logger as follow for our test suite :
export const queryClient = new QueryClient({
logger: {
log: console.log,
warn: console.warn,
error: () => {}
}
});
Mocking errors as follow within msw :
rest.get('/some/api/route', (_req, res, ctx) => {
return res.once(ctx.status(500), ctx.json({ errorMessage: 'some error' }));
})
We override the handlers for errors :
it("should return error", () => {
server.use(...errorHandlers);
const { result } = renderHook(() =>
query({
url: '/some/api/routes',
options: { retry: false }
})
);
...some assertions
});
Out tests are ok, but the network errors are still appearing in the console when running the test suite (the display is thrashed) :
Error: Request failed with status code 500
at createError (.../node_modules/axios/lib/core/createError.js:16:15)
at settle (.../node_modules/axios/lib/core/settle.js:17:12)
at XMLHttpRequestOverride.onloadend (.../node_modules/axios/lib/ad
... super long logging ...
Such non blocking issue is tackled in other projects using jest as the custom queryClient logger does the job; it seems that it behaves differently using vitest. Does anyone knows which part is the culprit in that context ? What should be done ?
passing a custom logger to the QueryClient is a feature that was introduced in v4.
For v3, you need to call setLogger, which is exported from react-query:
https://react-query-v3.tanstack.com/reference/setLogger
import { setLogger } from 'react-query'
setLogger({
log: console.log,
warn: console.warn,
error: () => {}
})

Network Error Error: connect ECONNREFUSED 127.0.0.1:5000 after a certain number of tests in react-testing library

I am testing an app with react-testing-library and I am using a mock service worker.
All my tests pass until the last one which gives this error in the title.
When testing only the part which gives error isolated (test.only), then it doesn't throw error.
The error points to localhost:5000 which is my data server (my app is running on 3000)
This is my last test which only works when running alone:
import { findByRole, getByRole, render, screen } from '#testing-library/react'
import userEvent from '#testing-library/user-event'
import Layout from '../../layout'
describe('tests for headers and content of the table', () => {
test('to check headers in inital state', async () => {
render(<Layout />)
const headerFirstCell = await screen.findByRole('columnheader', {name: /name/i, })
expect(headerFirstCell).toHaveTextContent('Name')
const headerSecondCell = await screen.findByRole('columnheader', {name: /courses/i,}) //second
expect(headerSecondCell).toHaveTextContent('Courses')
})
It is also strange that when I run only one part of the last code + all the rest, fex:
const headerFirstCell = await screen.findByRole('columnheader', {name: /name/i, })
expect(headerFirstCell).toHaveTextContent('Name')
Or the other part + all the rest:
const headerSecondCell = await screen.findByRole('columnheader', {name: /courses/i,}) //second
expect(headerSecondCell).toHaveTextContent('Courses')
Then all tests pass. It seems like any line of code I add after that point will make the error throw.
I know it might not be easy to see from this info...but I'm lost...Any clue??
Just in case this is my setupTests.js:
import '#testing-library/jest-dom'
import { server } from './mocks/server.js'
beforeAll(() => server.listen())
afterEach(() => server.resetHandlers())
afterAll(() => server.close())
Just was a typo in one of the server url addresses in my mock service worker handlers.
So when testing a part which gets requests to that url it threw an error sometimes, in case I place that function at the end, the tests finished before doing the get request to the wrong url. That's why it had that strange behaviour.

Axios in lambda not waiting for response

I am invoking axios post method in aws lambda. Most of the times lambda does not return any result.logs show the following results
START RequestId: ac92d268-d212-4b80-a06c-927922fcf1d5 Version: $LATEST
END RequestId: ac92d268-d212-4b80-a06c-927922fcf1d5
But some times lambda return expected results. Looks like lambda is not waiting for axios to complete. below is lambda code.
var axios = require('axios')
exports.handler = async (event, context,callback) => {
axios.post('https://example.com/testapi/api.asmx/GetNames', {})
.then((res) => {
console.log(JSON.stringify(res.data,null,2))
callback(null,'success');
})
.catch((error) => {
console.error(error)
callback(null,'error');
})
};
Your handler is async which means it will run asynchronously and return a Promise. This means that your function is being terminated before your code actually runs.
Since axios already works with Promises and your method already is async, you don't need to change too much. This will fix the problem:
const axios = require('axios')
exports.handler = async (event) => {
try {
const res = await axios.post('https://example.com/testapi/api.asmx/GetNames', {})
console.log(res)
return {
statusCode: 200,
body: JSON.stringify(res)
}
} catch (e) {
console.log(e)
return {
statusCode: 400,
body: JSON.stringify(e)
}
}
};
You can understand more around async/await if you want to.
I was having a similar issue where I make a 3rd party API call with Axios in Lambda, after spending almost a day noticed that my lambda had 6 seconds default timeout. Sometimes the response from the api was getting longer than 6 seconds and it was causing a 502 response.

socket.io-client Jest testing inconsistent results

I am writing some end-to-end test cases to test socket connections in my app. I expect receiving socket events after specific rest API requests. For instance, after hitting: /api/v1/[createTag], I expect receiving createTag event to be captured by socket.io-client. The issue is that, it is very inconsistently passing, and sometimes failing, with good rest API requests. The reason to fail is that done() event inside socket.on('createTag' ... is never called, so it gets timeout. On browser, currently all the API endpoints and sockets seem to be working fine. Is there a specific configuration that I might be missing in order to test socket.io-client within Node.js environment and Jest?
Below is my test cases, and thanks a lot in advance:
describe('Socket integration tests: ', () => {
beforeAll(async done => {
await apiInit();
const result = await requests.userSignIn(TEST_MAIL, TEST_PASSWORD);
TEST_USER = result.user;
SESSION = result.user.session;
console.log('Test user authenticated succesfully.');
done();
});
beforeEach(done => {
socket = io(config.socket_host, { forceNew: true })
socket.on('connect', () => {
console.log('Socket connection succesful.');
socket.emit('session', { data: SESSION }, (r) => {
console.log('Socket session successful.');
done();
});
});
})
test('Receiving createTag socket event?', async(done) => {
console.log('API request on createTag');
const response = await Requester.post(...);
console.log('API response on createTag', response);
socket.on('createTag', result => {
console.log('createTag socket event succesful.');
createdTagXid = result.data.xid;
done();
})
});
afterEach(done => {
if(socket.connected) {
console.log('disconnecting.');
socket.disconnect();
} else {
console.log('no connection to break');
}
done();
})
}
Basically, setting event handles after async API calls seems to be the issue. So I should have first set the socket.on( ... and then call rest API.

Unable to get Moxios stubRequest to work

I'm having issues getting stubRequest to work properly. Here's my code:
it('should stub my request', (done) => {
moxios.stubRequest('/authenticate', {
status: 200
})
//here a call to /authenticate is being made
SessionService.login('foo', 'bar')
moxios.wait(() => {
expect(something).toHaveHappened()
done()
})
})
This works fine:
it('should stub my request', (done) => {
SessionService.login('foo', 'bar')
moxios.wait(async () => {
let request = moxios.requests.mostRecent()
await request.respondWith({
status: 200
})
expect(something).toHaveHappened()
done()
})
})
The second method just get's the last call though, and I'd really like to be able to explicitely stub certain requests.
I'm running Jest with Vue.
I landed here with a similar goal and eventually solved it using a different approach that may be helpful to others:
moxios.requests has a method .get() (source code) that lets you grab a specific request from moxios.requests based on the url. This way, if you have multiple requests, your tests don't require the requests to occur in a specific order to work.
Here's what it looks like:
moxios.wait(() => {
// Grab a specific API request based on the URL
const request = moxios.requests.get('get', 'endpoint/to/stub');
// Stub the response with whatever you would like
request.respondWith(yourStubbedResponseHere)
.then(() => {
// Your assertions go here
done();
});
});
NOTE:
The name of the method .get() is a bit misleading. It can handle different types of HTTP requests. The type is passed as the first parameter like: moxios.requests.get(requestType, url)
it would be nice if you show us the service. Service call must be inside the moxios wait func and outside must be the axios call alone. I have pasted a simplified with stubRequest
describe('Fetch a product action', () => {
let onFulfilled;
let onRejected;
beforeEach(() => {
moxios.install();
store = mockStore({});
onFulfilled = sinon.spy();
onRejected = sinon.spy();
});
afterEach(() => {
moxios.uninstall();
});
it('can fetch the product successfully', done => {
const API_URL = `http://localhost:3000/products/`;
moxios.stubRequest(API_URL, {
status: 200,
response: mockDataSingleProduct
});
axios.get(API_URL, mockDataSingleProduct).then(onFulfilled);
const expectedActions = [
{
type: ACTION.FETCH_PRODUCT,
payload: mockDataSingleProduct
}
];
moxios.wait(function() {
const response = onFulfilled.getCall(0).args[0];
expect(onFulfilled.calledOnce).toBe(true);
expect(response.status).toBe(200);
expect(response.data).toEqual(mockDataSingleProduct);
return store.dispatch(fetchProduct(mockDataSingleProduct.id))
.then(() => {
var actions = store.getActions();
expect(actions.length).toBe(1);
expect(actions[0].type).toBe(ACTION.FETCH_PRODUCT);
expect(actions[0].payload).not.toBe(null || undefined);
expect(actions[0].payload).toEqual(mockDataSingleProduct);
expect(actions).toEqual(expectedActions);
done();
});
});
});
})