用 Jest 测试 Axios

Posted

技术标签:

【中文标题】用 Jest 测试 Axios【英文标题】:Testing Axios in Jest 【发布时间】:2020-11-24 08:21:35 【问题描述】:

我是测试新手。 我正在尝试测试异步数据获取功能,但我无法弄清楚为什么测试没有通过。 我开玩笑地模拟了 Axios,并为 Axios 的 get 方法提供了一个模拟实现来解决一个承诺。 错误说它无法读取 name 的属性,这意味着我认为数据 obj 未定义。

这里是 Yelp.test.js

import Yelp from './Yelp';
import axios from 'axios';

jest.mock('axios');

describe('searchRestaurantsInfo', () => 
  test('returns object with restaurant infos', async () => 
    const data = 
      name: 'Casa Romana',
      address: '5 Albion Street',
      coordinates:  lat: 52.6322649, lng: -1.1314474 ,
      city: 'Leicester LE1 6GD',
      rating: 4.5,
      photos: [
        'https://s3-media1.fl.yelpcdn.com/bphoto/4VUq4j1FF-n5bgXjtoC0Xw/o.jpg',
        'https://s3-media1.fl.yelpcdn.com/bphoto/4VUq4j1FF-n5bgXjtoC0Xw/o.jpg',
        'https://s3-media1.fl.yelpcdn.com/bphoto/4VUq4j1FF-n5bgXjtoC0Xw/o.jpg',
      ],
      phone: '+441162541174',
      price: '£££',
      categories: 'Italian',
      url:
        'https://www.yelp.com/biz/casa-romana-leicester?adjust_creative=7GHt4FY-2vjNyIPhQV7wcw&utm_campaign=yelp_api_v3&utm_medium=api_v3_business_lookup&utm',
      reviews: [
        
          id: 'i_Q39aN9hwZzGDUb-IWpYw',
          rating: 5,
          text:
            'Proper Italian restaurant. Not Italian-themed, or serving Italian fusion cuisine, just a place with an Italian owner who makes solid, straightforward...',
          time_created: '2014-10-02 03:49:36',
          url:
            'https://www.yelp.com/biz/casa-romana-leicester?adjust_creative=7GHt4FY-2vjNyIPhQV7wcw&hrid=i_Q39aN9hwZzGDUb-IWpYw&utm_campaign=yelp_api_v3&utm_me',
          user: 
            id: '6tPD46XZSFllvgn2vTh51A',
            image_url:
              'https://s3-media3.fl.yelpcdn.com/photo/A4Ww6Ks2P9WsALqOFy9cOA/o.jpg',
            name: 'Espana S.',
            profile_url:
              'https://www.yelp.com/user_details?userid=6tPD46XZSFllvgn2vTh51A',
          ,
        ,
      ],
    ;

    axios.get.mockImplementationOnce(() => Promise.resolve(data));

    await expect(
      Yelp.searchRestaurantsInfo('q_IoMdeM57U70GwqjXxGJw')
    ).resolves.toEqual(data);
  );
);

还有 Yelp.js

import axios from 'axios';

let YELP_API_KEY = process.env.REACT_APP_YELP_API_KEY;

const Yelp = 
   // Provides infos about a single restaurant
  async searchRestaurantsInfo(id) 
    try 
      let response = await axios.get(
        `https://cors-anywhere.herokuapp.com/https://api.yelp.com/v3/businesses/$id`,
        
          headers: 
            Authorization: `Bearer $YELP_API_KEY`,
            'X-Requested-With': 'XMLHttpRequest',
            'Access-Control-Allow-Origin': '*',
          ,
        
      );

      let responseRew = await axios.get(
        `https://cors-anywhere.herokuapp.com/https://api.yelp.com/v3/businesses/$id/reviews`,
        
          headers: 
            Authorization: `Bearer $YELP_API_KEY`,
            'X-Requested-With': 'XMLHttpRequest',
            'Access-Control-Allow-Origin': '*',
          ,
        
      );

      const parameters = 
        name: response.data.name,
        address: response.data.location.display_address[0],
        coordinates: 
          lat: response.data.coordinates.latitude,
          lng: response.data.coordinates.longitude,
        ,
        city: response.data.location.display_address[1],
        rating: response.data.rating,
        photos: response.data.photos,
        phone: response.data.phone,
        price: response.data.price,
        categories: response.data.categories[0].title,
        url: response.data.url,
        reviews: responseRew.data.reviews,
      ;
      console.log( parameters, id );

      return parameters;
     catch (e) 
      console.log(e);
      return e;
    
  

我得到的错误是

searchRestaurantsInfo
    × returns array of restaurnats obj (66ms)

  ● searchRestaurantsInfo › returns array of restaurnats obj

    expect(received).resolves.toEqual(expected) // deep equality

    - Expected
    + Received

    - Object // data object. I removed it from this error message because too long
    + [TypeError: Cannot read property 'name' of undefined]

      47 |     await expect(
      48 |       Yelp.searchRestaurantsInfo('q_IoMdeM57U70GwqjXxGJw')
    > 49 |     ).resolves.toEqual(data);
         |                ^
      50 |   );
      51 | );
      52 | 

      at Object.toEqual (node_modules/react-scripts/node_modules/expect/build/index.js:202:20)
      at Object.<anonymous> (src/helpers/Yelp.test.js:49:16)

  console.log src/helpers/Yelp.js:91
    TypeError: Cannot read property 'name' of undefined
        at Object.searchRestaurantsInfo (C:\Users\Turi\Desktop\project\RestaurantsRedux\src\helpers\Yelp.js:72:29)
        at processTicksAndRejections (internal/process/task_queues.js:97:5)
        at Object.<anonymous> (C:\Users\Turi\Desktop\project\RestaurantsRedux\src\helpers\Yelp.test.js:47:5)

提前感谢您的帮助!

【问题讨论】:

我只是在这里猜测,但也许你需要在那些地方传递 data 而不是 data @tanmay 不,同样的错误。 应该是axios.get.mockImplementationOnce(() =&gt; Promise.resolve( data )。有 2 个请求,但只有一个模拟,应该有更多。顺便说一句,捕捉错误并返回它不是常规的。 【参考方案1】:

您等待结果的方式可能有问题(可能是编译问题),请尝试像这样编写测试。

// note make sure the test() function is async

const result = await Yelp.searchRestaurantsInfo('q_IoMdeM57U70GwqjXxGJw')
      expect(result).toEqual(data);

【讨论】:

【参考方案2】:

我已经设法找到了解决方案。

就像建议的那样,我必须添加另一个模拟,因为在函数中有两个不同的请求。

除此之外,我意识到我不能同时使用data

axios.get.mockImplementationOnce(() =&gt; Promise.resolve(data));

      Yelp.searchRestaurantsInfo('q_IoMdeM57U70GwqjXxGJw')
    ).resolves.toEqual(data);```

因为该函数没有返回data,而是一个包含data 中某些部分的对象。 因此我创建了一个新对象params 与函数返回的对象进行比较。

import Yelp from './Yelp';
import axios from 'axios';

jest.mock('axios');

describe('searchRestaurantsInfo', () => 
  test('returns object with restaurant infos', async () => 
    const response = 
      data: 
        name: 'Casa Romana',
        location: 
          display_address: [
            "12 Upper Saint Martin's Lane",
            'London WC2H 9FB',
            'United Kingdom',
          ],
        ,
        coordinates:  latitude: 52.6322649, longitude: -1.1314474 ,

        rating: 4.5,
        photos: [
          'https://s3-media1.fl.yelpcdn.com/bphoto/4VUq4j1FF-n5bgXjtoC0Xw/o.jpg',
          'https://s3-media1.fl.yelpcdn.com/bphoto/4VUq4j1FF-n5bgXjtoC0Xw/o.jpg',
          'https://s3-media1.fl.yelpcdn.com/bphoto/4VUq4j1FF-n5bgXjtoC0Xw/o.jpg',
        ],
        phone: '+441162541174',
        price: '£££',
        categories: [ alias: 'indpak', title: 'Indian' ],
        url:
          'https://www.yelp.com/biz/casa-romana-leicester?adjust_creative=7GHt4FY-2vjNyIPhQV7wcw&utm_campaign=yelp_api_v3&utm_medium=api_v3_business_lookup&utm',
      ,
    ;

    const responseRev = 
      data: 
        reviews: [
          
            id: 'i_Q39aN9hwZzGDUb-IWpYw',
            rating: 5,
            text:
              'Proper Italian restaurant. Not Italian-themed, or serving Italian fusion cuisine, just a place with an Italian owner who makes solid, straightforward...',
            time_created: '2014-10-02 03:49:36',
            url:
              'https://www.yelp.com/biz/casa-romana-leicester?adjust_creative=7GHt4FY-2vjNyIPhQV7wcw&hrid=i_Q39aN9hwZzGDUb-IWpYw&utm_campaign=yelp_api_v3&utm_me',
            user: 
              id: '6tPD46XZSFllvgn2vTh51A',
              image_url:
                'https://s3-media3.fl.yelpcdn.com/photo/A4Ww6Ks2P9WsALqOFy9cOA/o.jpg',
              name: 'Espana S.',
              profile_url:
                'https://www.yelp.com/user_details?userid=6tPD46XZSFllvgn2vTh51A',
            ,
          ,
        ],
      ,
    ;

    const params = 
      name: 'Casa Romana',
      address: "12 Upper Saint Martin's Lane",
      coordinates:  lat: 52.6322649, lng: -1.1314474 ,
      city: 'London WC2H 9FB',
      rating: 4.5,
      photos: [
        'https://s3-media1.fl.yelpcdn.com/bphoto/4VUq4j1FF-n5bgXjtoC0Xw/o.jpg',
        'https://s3-media1.fl.yelpcdn.com/bphoto/4VUq4j1FF-n5bgXjtoC0Xw/o.jpg',
        'https://s3-media1.fl.yelpcdn.com/bphoto/4VUq4j1FF-n5bgXjtoC0Xw/o.jpg',
      ],
      phone: '+441162541174',
      price: '£££',
      categories: 'Indian',
      url:
        'https://www.yelp.com/biz/casa-romana-leicester?adjust_creative=7GHt4FY-2vjNyIPhQV7wcw&utm_campaign=yelp_api_v3&utm_medium=api_v3_business_lookup&utm',
      reviews: [
        
          id: 'i_Q39aN9hwZzGDUb-IWpYw',
          rating: 5,
          text:
            'Proper Italian restaurant. Not Italian-themed, or serving Italian fusion cuisine, just a place with an Italian owner who makes solid, straightforward...',
          time_created: '2014-10-02 03:49:36',
          url:
            'https://www.yelp.com/biz/casa-romana-leicester?adjust_creative=7GHt4FY-2vjNyIPhQV7wcw&hrid=i_Q39aN9hwZzGDUb-IWpYw&utm_campaign=yelp_api_v3&utm_me',
          user: 
            id: '6tPD46XZSFllvgn2vTh51A',
            image_url:
              'https://s3-media3.fl.yelpcdn.com/photo/A4Ww6Ks2P9WsALqOFy9cOA/o.jpg',
            name: 'Espana S.',
            profile_url:
              'https://www.yelp.com/user_details?userid=6tPD46XZSFllvgn2vTh51A',
          ,
        ,
      ],
    ;

    axios.get.mockImplementationOnce(() => Promise.resolve(response));
    axios.get.mockImplementationOnce(() => Promise.resolve(responseRev));

    await expect(
      Yelp.searchRestaurantsInfo('q_IoMdeM57U70GwqjXxGJw')
    ).resolves.toEqual(params);
  );
);

【讨论】:

以上是关于用 Jest 测试 Axios的主要内容,如果未能解决你的问题,请参考以下文章

Nest JS - 为返回 Observable Axios 响应的函数编写 Jest 测试用例的问题

如何使用 jest 测试 axios 拦截器?

如何用 jest 测试 axios 拦截器

如何在 React 应用程序中使用 JEST 测试向 api 发出 axios 请求的异步函数

Redux 使用 jest、nock、axios 和 jsdom 测试 multipart/form-data

在 Jest 中测试 Global Vue.prototype.$http 方法,它在 Vue 中使用 axios