模拟服务工作者,可悲的路径测试失败

mock service worker, sad path test failure

最近只是使用 Mock Service Worker 来测试我的 HTTP 请求,我正在寻找测试我的失败路径。

我的第一个测试通过了(很高兴),但是我收到的失败错误是“JSON 输入的意外结束”

它确实按照我想要的方式运行,但从测试的角度来看我有点困惑。

如何让我的失败路径通过测试?

我的测试文件

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");
});

我的获取异步函数

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 
}

修复模拟服务器

问题是 collect 函数期望 JSON 响应,即使在出现错误的情况下也是如此,但您的模拟服务器不会 return 响应。因此,当您在 collect 函数中执行 res.json() 时,您会得到一个错误。

将您的响应解析器更新为 return json 响应。

return res(ctx.json({message: "error"}), ctx.status(401));

修复测试

toThrow 在这里不是正确的匹配器,因为 async 函数总是 return promise 而在你的情况下 collect 函数 returns 是一个承诺被抛出的数据拒绝。
因此,您可以改用 toEqual 匹配器。

您还需要更新测试错误的方式。您可以选择以下任何选项:

使用 rejects 匹配器:

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 });
});

使用async/await语法:

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 });
  }
});

使用.catch

但在这种方法中,您需要明确检查您的 catch 断言是否已被调用,否则已实现的承诺不会使您的测试失败。

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 函数

在您的 collect 函数中,status 应该是 res.status 而不是 data.code

您还可以通过将 res.json() 调用移出条件语句来稍微清理一下下面的代码。

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;
};

而且你不应该在反应环境变量中存储秘密,那样会暴露。 Docs

在一些帮助下,我更新了我的收集功能,如下所示 我现在发送响应值作为状态

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;
};

我的测试现在看起来像这样,我不得不使用 toEqual 匹配器而不是 toThrow 并使用它 return 而不是 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 });
});