articles icon indicating copy to clipboard operation
articles copied to clipboard

单元测试系列 -- jest常用mock

Open zhengguorong opened this issue 6 years ago • 0 comments

进行单元测试时,我们希望得到的数据是稳定可控的,例如应用涉及调用网络接口数据,可以通过mock的方法控制数据的返回。

下面为获取电影列表数据的业务代码 页面在onLoad时执行getComingFilm方法,getComingFilm方法获取网络数据。

const filmServer = require('../../server/film.js');

Page({
  data: {
    comingFilms: [],
  },
  onLoad() {
    this.getComingFilm();
  },
  // 获取即将上映电影列表
  getComingFilm() {
    return filmServer.getComingSoon(1, 5).then((data) => {
      data.films.forEach((film) => {
        const displayDate = `${new Date(film.premiereAt).getMonth() + 1}月${new Date(film.premiereAt).getDate()}日`;
        film.displayDate = displayDate;
      });
      this.setData({ comingFilms: data.films });
    });
  },
});

我们需要编写两个单元测试保障代码。 1、保障onLoad时执行getComingFilm方法。 2、保障getComingFilm后日期数据进行格式化。

使用spyOn保留方法原定义逻辑和增加mock方法

下面的单元测试用来保障onLoad时调用getComingFilm方法,但是你发现jest会提示getComingFilm为非mock对象,不能调用toBeCalled函数。

it('should getComingFilm', () => {
    page.onLoad();
    expect(page.getComingFilm).toBeCalled();
});

那我们把getComingFilm方法转换成mock对象

it('should getComingFilm', () => {
    page.getComingFilm = jest.fn();
    page.onLoad();
    expect(page.getComingFilm).toBeCalled();
});

以上写法虽然能调用toBeCalled方法了,但原来的getComingFilm方法被覆盖,如果后续要用到该方法,将无法执行正确逻辑。 可以使用spyOn方法,既保留方法原先定义,又让方法具备mock方法。

it('should getComingFilm', () => {
    jest.spyOn(page, 'getComingFilm');
    page.onLoad();
    expect(page.getComingFilm).toBeCalled();
});

mock网络请求

const filmServer = require('../../server/film.js');

getComingFilm() {
  return filmServer.getComingSoon(1, 5).then((data) => {

  });
});

上面的业务代码,通过filmServer进行网络请求,获取相应业务数据。 我们希望测试代码中使用mock数据,可以在server目录下创建__mocks__文件夹并创建film.js文件。

server/mocks/film.js 文件代码如下

const filmService = jest.mock('../film.js');

const comingSoonRes = { films: [{ id: 1, name: 'test', premiereAt: Date.now() }], page: { total: 10, current: 1 } };
filmService.getComingSoon = jest.fn(() => Promise.resolve(comingSoonRes));

module.exports = {
  getComingSoon: filmService.getComingSoon,
  comingSoonRes,
};

在你的测试文件加入mock声明,原网络请求接口就会被替换。

// 使用mock文件
jest.mock('../../server/film.js');
// getComingFilm将返回server/__mocks__/film.js中定义的数据
it('should format premiereAt as MM月DD日 ', () => page.getComingFilm().then(() => {
    expect(page.data.comingFilms[0].displayDate).toEqual('9月12日');
}));

注意:premiereAt字段使用了Date.now() 创建时间戳,但是因为该方法会随着执行时间变化,需要控制该方法的返回值

通过global设置方法的固定返回。

global.Date.now = jest.fn(() => 1536708613825);

🌟由于代码较多,上面只截取了部分,完整代码可以访问github获取

zhengguorong avatar Sep 12 '18 23:09 zhengguorong