【问题标题】:Jest and HTTP mocking with nock用 nock 进行 Jest 和 HTTP 模拟
【发布时间】:2021-11-05 21:50:36
【问题描述】:

我试图在我的 Next.js 应用程序中为端点编写我的第一个 Jest 测试,无论我如何尝试“破坏”它,这个测试总是通过。这让我想,我做错了。这是我的 api/weather.js 端点:

const url = (city) => `https://api.weatherapi.com/v1/current.json?key=${process.env.WEATHER_API_KEY}&q=${city}`;
export default async function handler(req, res) {
 const { query: { city } } = req
  return fetch(url(city))
    .then((response) => {
      if(response.ok){
        return response.json()
      }
      throw new Error('Response not OK')
    })
    .then((data) => res.status(200).json(data))
    .catch(() => res.status(400).json({message: 'Currently not avaliable'}))
}

所以基本上我的前端向 api/weather 发出了一个请求,看起来有点像

const fetchCityData = () => {
    const options = {
      method: `POST`,
    };
    fetch(`/api/weather?city=${city}`, options)
    .then((response) => { ......

我有一个测试端点的任务,我知道它应该测试 api/weather.js,因为这取决于我需要模拟请求的外部 API。我还是有点迷失在这里。另外,我使用查询字符串,我试图将其集成到我的笑话测试中,但不确定我在做什么

import nock from 'nock';
it('should return weather', () => {
    nock('https://api.weatherapi.com/v1')
    .get(`/current.json?key=123434&q=London`)
    .reply(200, { results: [{ temp_c: '18 degrees' }] })
});

基本上,“现实生活”中发生的事情是,我在前端的输入中键入一个城市,该城市被发布到 api/weather.js,然后它将返回该城市的天气。我该如何测试它?我已经读了 3 天关于 nock and jest 的文章,但我真的不明白它背后的概念。另外,如果我将测试中的 .reply 重写为 400 或重写结果,测试仍然会通过。为什么?我做错了什么?

【问题讨论】:

    标签: unit-testing jestjs next.js nock


    【解决方案1】:

    您传递给 nock 的 .reply 的第二个参数将创建一个拦截器,该拦截器将在请求持续到您设置的主机时更改请求。这意味着通过这样做:

        it('should return weather', () => {
           nock('https://api.weatherapi.com/v1')
           .get(`/current.json?key=123434&q=London`)
           .reply(200, { results: [{ temp_c: '18 degrees' }] })
        });
    

    下一个 fetch 调用将返回一个由它组成的对象:

       const { results } = await fetchCityData("London");
       expect(results[0].temp_c).toEqual("19 degrees");
    

    那会过去的。这不会:

        const { results } = await fetchCityData("London");
        expect(results[0].temp_c).toEqual("33 degrees");
    
       Expected: "33 degrees"
       Received: "19 degrees"
    

    然后由您决定使用您想从 WeatherAPi 模拟的所有数据来完全模拟一个正确的请求,例如:

    [...]
     {
          location: {
            name: 'London',
            region: 'City of London, Greater London',
            country: 'United Kingdom',
            lat: 51.52,
            lon: -0.11,
            tz_id: 'Europe/London',
            localtime_epoch: 1631181607,
            localtime: '2021-09-09 11:00'
          },
          current: {
    [...]
    

    或尝试使用Nock's recording 选项,这些选项可让您在想要抓取的数据上来回切换。

    这是dominicfraser/NockExamples 的一个很好的例子,它将涵盖一些对您有用的用例并提供一些指导,然后从那里开始!

    编辑 1: 下面是一些希望能澄清一些东西的代码:

    import nock from "nock";
    const fetch = require('node-fetch');
    
    const SERVER_HOST = 'http://localhost:3000';
    
    // from WeatherApi.com
    const londonMock = {"location":{"name":"London","region":"City of London, Greater London","country":"United Kingdom",
    "lat":51.52,"lon":-0.11,"tz_id":"Europe/London","localtime_epoch":1631195008,
    "localtime":"2021-09-09 14:43"},"current":{"last_updated_epoch":
    1631194200,"last_updated":"2021-09-09 14:30","temp_c":23,"temp_f":73.4,"is_day":1,
    "condition":{"text":"Partly cloudy","icon":"//cdn.weatherapi.com/weather/64x64/day/116.png","code":1003},
    "wind_mph":11.9,"wind_kph":19.1,"wind_degree":210,"wind_dir":"SSW","pressure_mb":1008,
    "pressure_in":29.77,"precip_mm":0,"precip_in":0,"humidity":69,"cloud":75,"feelslike_c":25,
    "feelslike_f":77,"vis_km":10,"vis_miles":6,"uv":5,"gust_mph":9.6,"gust_kph":15.5}};
    
    const fetchCityData = async (city) => {
      const options = {
        method: `GET`,
      };
      const response = await fetch(`${SERVER_HOST}/api/weather?city=${city}`, options);
      const data = await response.json();
      return data;
    }
    
    it("checks if London's weather equals to 33 degrees on a mocked response", async () => {
        nock(SERVER_HOST)
          .get(`/api/weather?city=London`)
          .reply(200, londonMock);
        const results = await fetchCityData("London");
        expect(results.current.temp_c).toEqual(33);
    });
    
    it("checks if London's weather equals to 23 degrees on a mocked response", async () => {
        nock(SERVER_HOST)
          .get(`/api/weather?city=London`)
          .reply(200, londonMock);
        const results = await fetchCityData("London");
        expect(results.current.temp_c).toEqual(23);
    });
    

    【讨论】:

    • 我还是不太明白:.reply(200, { results: [{ temp_c: '18 degrees' }] }) 并没有真正反映响应,因为对于伦敦电流来说,它看起来是这样的:current: {last_updated_epoch: 1631190600, last_updated: "2021-09-09 13:30", temp_c: 22,... - 应该失败,但它没有。为什么?
    • 这也应该失败.reply(200, { results: [{ current: expect.any(String), }] }),但我在那里写的每一个废话都通过了
    • 你把东西弄混了。通过 nock 的 .reply() 调用会改变您将通过 fetch 对同一主机 + 路径进行的下一个 GET/POST 调用的响应。我会在上面的编辑中写下一些代码,这样可能会更清楚。
    • 谢谢。是的,我完全迷路了
    猜你喜欢
    • 2015-12-16
    • 2019-09-01
    • 2018-08-29
    • 2017-07-24
    • 2021-05-31
    • 2015-08-05
    • 2017-04-15
    • 2021-01-13
    • 2023-04-02
    相关资源
    最近更新 更多