Reputation: 81
Recently just using Mock Service Worker to test my HTTP requests, i'm looking to test my failure path.
My first test passes (happy), but the error i'm receiving for my failure is "Unexpected end of JSON input"
It does behave in the way that I would like, but from a testing point of view i'm a little confused.
How can I get my failure path to pass the test?
My test file
import "whatwg-fetch";
import { rest } from "msw";
import { setupServer } from "msw/node";
import { collect } from "./collect";
const server = setupServer(
rest.get(
"http://api.openweathermap.org/data/2.5/weather",
(req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({ base: "stations", clouds: { all: 6 }, cod: 200 })
);
}
)
);
beforeAll(() => server.listen());
afterAll(() => server.close());
afterEach(() => server.resetHandlers());
it("collects data", async () => {
const res = await collect();
expect(res).toEqual({ base: "stations", clouds: { all: 6 }, cod: 200 });
});
it("handles failure", async () => {
server.use(
rest.get(
"http://api.openweathermap.org/data/2.5/weather",
(req, res, ctx) => {
return res(ctx.status(401));
}
)
);
await expect(collect()).rejects.toThrow("401");
});
My fetch async function
require('dotenv').config()
export const collect = async () => {
const key = process.env.REACT_APP_API_KE
// try{
const res = await fetch(`http://api.openweathermap.org/data/2.5/weather?q=london&appid=${key}`)
if(res.status !== 200){
const error = await res.json()
throw { message: error.message, status: error.cod }
}
const data = await res.json()
return data
}
Upvotes: 2
Views: 3655
Reputation: 8188
The problem is that the collect
function is expecting a JSON response even in case of an error, but your mock server doesn't return that. So, when you do res.json()
in your collect
function you get an error.
Update your response resolver to return a json
response.
return res(ctx.json({message: "error"}), ctx.status(401));
toThrow
is not the correct matcher here, because async
functions always return promise and in your case the collect
function returns a promise that gets rejected with the thrown data.
So, you can use the toEqual
matcher instead.
Also you need to update the way the error is tested. You can opt any of the following options:
Using rejects
matcher:
it("handles failure", () => {
server.use(
rest.get(
"http://api.openweathermap.org/data/2.5/weather",
(req, res, ctx) => {
return res(ctx.json({message: "error"}), ctx.status(401));
}
)
);
return expect(collect()).rejects.toEqual({ message: "error", status: 401 });
});
Using async/await
syntax:
it("handles failure", async () => {
server.use(
rest.get(
"http://api.openweathermap.org/data/2.5/weather",
(req, res, ctx) => {
return res(ctx.status(401));
}
)
);
try {
await collect();
} catch (err) {
expect(err).toEqual({ message: "error", status: 401 });
}
});
Using .catch
But in this approach you need to explicitly check that your catch
assertion has been called, otherwise a fulfilled promise will not fail your test.
it("handles failure", async () => {
server.use(
rest.get(
"http://api.openweathermap.org/data/2.5/weather",
(req, res, ctx) => {
return res(ctx.status(401));
}
)
);
expect.assertions(1);
return collect().catch((err) =>
expect(err).toEqual({ message: "error", status: 401 })
);
});
collect
functionIn your collect
function the status
should be res.status
and not data.code
.
And you can also clean the following code a bit by moving the res.json()
call out of the conditional.
require("dotenv").config();
export const collect = async () => {
const key = process.env.REACT_APP_API_KEY;
const res = await fetch(
`http://api.openweathermap.org/data/2.5/weather?q=london&appid=${key}`
);
const data = await res.json();
if (res.status !== 200) {
throw { message: data.message, status: res.status };
}
return data;
};
And also you shouldn't be storing secrets in react environment variables, that would be exposed. Docs
Upvotes: 3
Reputation: 81
With some help I updated my collect function so it was the following I'm now sending the value of the response as the status
require("dotenv").config();
export const collect = async () => {
const key = process.env.REACT_APP_API_KEY;
const res = await fetch(
`http://api.openweathermap.org/data/2.5/weather?q=london&appid=${key}`
);
const data = await res.json();
if (res.status !== 200) {
throw { message: data.message, status: res.status };
}
return data;
};
My test now looks like this, I had to use the toEqual matcher instead of toThrow and have it return instead of await / async
it("handles failure", () => {
server.use(
rest.get(
"http://api.openweathermap.org/data/2.5/weather",
(req, res, ctx) => {
return res(ctx.json({message: "error"}), ctx.status(401));
}
)
);
return expect(collect()).rejects.toEqual({ message: "error", status: 401 });
});
Upvotes: 0