视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
如何利用Jest测试JavaScript(Mock函数)
2020-11-27 19:29:47 责编:小采
文档
 本篇文章给大家带来的内容是关于如何利用Jest测试JavaScript(Mock函数),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

在本篇教程中,我们会介绍 Jest 中的三个与 Mock 函数相关的API,分别是jest.fn()、jest.spyOn()、jest.mock()。使用它们创建Mock函数能够帮助我们更好的测试项目中一些逻辑较复杂的代码,例如测试函数的嵌套调用,回调函数的调用等。

如果你还不知道Jest的基本使用方法,请先阅读: http://www.gxlcms.com/js-tutorial-411835.html

为什么要使用Mock函数?

在项目中,一个模块的方法内常常会去调用另外一个模块的方法。在单元测试中,我们可能并不需要关心内部调用的方法的执行过程和结果,只想知道它是否被正确调用即可,甚至会指定该函数的返回值。此时,使用Mock函数是十分有必要。

Mock函数提供的以下三种特性,在我们写测试代码时十分有用:

  • 捕获函数调用情况

  • 设置函数返回值

  • 改变函数的内部实现

  • 我们接着使用上篇文章中的目录结构,在test/functions.test.js文件中编写测试代码,src/目录下写被测试代码。

    1. jest.fn()

    jest.fn()是创建Mock函数最简单的方式,如果没有定义函数内部的实现,jest.fn()会返回undefined作为返回值。

    // functions.test.js
    
    test('测试jest.fn()调用', () => {
     let mockFn = jest.fn();
     let result = mockFn(1, 2, 3);
    
     // 断言mockFn的执行后返回undefined
     expect(result).toBeUndefined();
     // 断言mockFn被调用
     expect(mockFn).toBeCalled();
     // 断言mockFn被调用了一次
     expect(mockFn).toBeCalledTimes(1);
     // 断言mockFn传入的参数为1, 2, 3
     expect(mockFn).toHaveBeenCalledWith(1, 2, 3);
    })

    jest.fn()所创建的Mock函数还可以设置返回值,定义内部实现或返回Promise对象。

    // functions.test.js
    
    test('测试jest.fn()返回固定值', () => {
     let mockFn = jest.fn().mockReturnValue('default');
     // 断言mockFn执行后返回值为default
     expect(mockFn()).toBe('default');
    })
    
    test('测试jest.fn()内部实现', () => {
     let mockFn = jest.fn((num1, num2) => {
     return num1 * num2;
     })
     // 断言mockFn执行后返回100
     expect(mockFn(10, 10)).toBe(100);
    })
    
    test('测试jest.fn()返回Promise', async () => {
     let mockFn = jest.fn().mockResolvedValue('default');
     let result = await mockFn();
     // 断言mockFn通过await关键字执行后返回值为default
     expect(result).toBe('default');
     // 断言mockFn调用后返回的是Promise对象
     expect(Object.prototype.toString.call(mockFn())).toBe("[object Promise]");
    })

    上面的代码是jest.fn()提供的几个常用的API和断言语句,下面我们在src/fetch.js文件中写一些被测试代码,以更加接近业务的方式来理解Mock函数的实际应用。

    被测试代码中依赖了axios这个常用的请求库和JSONPlaceholder这个上篇文章中提到免费的请求接口,请先在shell中执行npm install axios --save安装依赖。

    // fetch.js
    
    import axios from 'axios';
    
    export default {
     async fetchPostsList(callback) {
     return axios.get('https://jsonplaceholder.typicode.com/posts').then(res => {
     return callback(res.data);
     })
     }
    }

    我们在fetch.js中封装了一个fetchPostsList方法,该方法请求了JSONPlaceholder提供的接口,并通过传入的回调函数返回处理过的返回值。如果我们想测试该接口能够被正常请求,只需要捕获到传入的回调函数能够被正常的调用即可。下面是functions.test.js中的测试的代码。

    import fetch from '../src/fetch.js'
    
    test('fetchPostsList中的回调函数应该能够被调用', async () => {
     expect.assertions(1);
     let mockFn = jest.fn();
     await fetch.fetchPostsList(mockFn);
    
     // 断言mockFn被调用
     expect(mockFn).toBeCalled();
    })

    2. jest.mock()

    fetch.js文件夹中封装的请求方法可能我们在其他模块被调用的时候,并不需要进行实际的请求(请求方法已经通过单侧或需要该方法返回非真实数据)。此时,使用jest.mock()去mock整个模块是十分有必要的。

    下面我们在src/fetch.js的同级目录下创建一个src/events.js。

    // events.js
    
    import fetch from './fetch';
    
    export default {
     async getPostList() {
     return fetch.fetchPostsList(data => {
     console.log('fetchPostsList be called!');
     // do something
     });
     }
    }

    functions.test.js中的测试代码如下:

    // functions.test.js
    
    import events from '../src/events';
    import fetch from '../src/fetch';
    
    jest.mock('../src/fetch.js');
    
    test('mock 整个 fetch.js模块', async () => {
     expect.assertions(2);
     await events.getPostList();
     expect(fetch.fetchPostsList).toHaveBeenCalled();
     expect(fetch.fetchPostsList).toHaveBeenCalledTimes(1);
    });

    在测试代码中我们使用了jest.mock('../src/fetch.js')去mock整个fetch.js模块。如果注释掉这行代码,执行测试脚本时会出现以下报错信息

    从这个报错中,我们可以总结出一个重要的结论:

    在jest中如果想捕获函数的调用情况,则该函数必须被mock或者spy!

    3. jest.spyOn()

    jest.spyOn()方法同样创建一个mock函数,但是该mock函数不仅能够捕获函数的调用情况,还可以正常的执行被spy的函数。实际上,jest.spyOn()是jest.fn()的语法糖,它创建了一个和被spy的函数具有相同内部代码的mock函数。

    上图是之前jest.mock()的示例代码中的正确执行结果的截图,从shell脚本中可以看到console.log('fetchPostsList be called!');这行代码并没有在shell中被打印,这是因为通过jest.mock()后,模块内的方法是不会被jest所实际执行的。这时我们就需要使用jest.spyOn()。

    // functions.test.js
    
    import events from '../src/events';
    import fetch from '../src/fetch';
    
    test('使用jest.spyOn()监控fetch.fetchPostsList被正常调用', async() => {
     expect.assertions(2);
     const spyFn = jest.spyOn(fetch, 'fetchPostsList');
     await events.getPostList();
     expect(spyFn).toHaveBeenCalled();
     expect(spyFn).toHaveBeenCalledTimes(1);
    })

    执行npm run test后,可以看到shell中的打印信息,说明通过jest.spyOn(),fetchPostsList被正常的执行了。

    4. 总结

    这篇文章中我们介绍了jest.fn(),jest.mock()和jest.spyOn()来创建mock函数,通过mock函数我们可以通过以下三个特性去更好的编写我们的测试代码:

  • 捕获函数调用情况

  • 设置函数返回值

  • 改变函数的内部实现

  • 在实际项目的单元测试中,jest.fn()常被用来进行某些有回调函数的测试;jest.mock()可以mock整个模块中的方法,当某个模块已经被单元测试100%覆盖时,使用jest.mock()去mock该模块,节约测试时间和测试的冗余度是十分必要;当需要测试某些必须被完整执行的方法时,常常需要使用jest.spyOn()。这些都需要开发者根据实际的业务代码灵活选择。

    下载本文
    显示全文
    专题