首页 > 解决方案 > 如何使用 jest 创建一个单元测试用例来为我的去抖动和节流功能获得最大的代码覆盖率?

问题描述

我需要为库编写单元测试以获得最大的代码覆盖率——行覆盖率和分支覆盖率。

export const throttle = (fn, delay) => {
    let last = 0;
    return (...args) => {
        const now = new Date().getTime();
        if(now - last < delay) {
            return;
        }
        last = now;
        return fn(...args);
    }
}


export const debounce = ( fn, delay) => {
    let timeoutID;
    return function(...args){
        if (timeoutID){
            clearTimeout(timeoutID);
        }
        timeoutID = setTimeout( () => {
            fn(...args);  
        }, delay);
    };
}

import { throttle} from './throttle'
import { debounce} from './debounce'


document.getElementById('myid').addEventListener( "click", debounce(e => {
    console.log('clicked');
}, 2000))



document.getElementById('myid2').addEventListener('click', throttle(() => {
    console.log('you clicked me');
}, 5000));

我有以下被导出到主 js 页面的谴责和限制函数。我对如何为此使用 jest 编写测试用例感到困惑。我使用 lodash 阅读的每个问题,但我只需要一个简单的单元测试用例来处理这两个函数。我是开玩笑的新手,在阅读文档后,我不明白谴责和扼杀的“期望”是什么。谁能帮助我并提供一些澄清?

标签: javascriptunit-testingjestjsthrottlingdebounce

解决方案


你不需要 DOM 相关的东西来进行单元测试。对于测试throttle,您应该模拟Date. 对于测试debounce,您应该使用假计时器。Date并且setTimeout有副作用,我们应该模拟它们以消除副作用。

throttle.js

export const throttle = (fn, delay) => {
  let last = 0;
  return (...args) => {
    const now = new Date().getTime();
    if (now - last < delay) {
      return;
    }
    last = now;
    return fn(...args);
  };
};

throttle.test.js

import { throttle } from './throttle';

describe('65593662', () => {
  describe('throttle', () => {
    afterEach(() => {
      jest.restoreAllMocks();
    });
    it('should call function if now - last > delay', () => {
      const fn = jest.fn();
      const throttledFn = throttle(fn, 1000);
      const getTimeSpy = jest.spyOn(Date.prototype, 'getTime').mockReturnValue(2000);
      throttledFn('param');
      expect(fn).toBeCalledWith('param');
      expect(getTimeSpy).toBeCalledTimes(1);
    });

    it('should call function if now - last = delay', () => {
      const fn = jest.fn();
      const throttledFn = throttle(fn, 1000);
      const getTimeSpy = jest.spyOn(Date.prototype, 'getTime').mockReturnValue(1000);
      throttledFn('param');
      expect(fn).toBeCalledWith('param');
      expect(getTimeSpy).toBeCalledTimes(1);
    });

    it('should not call function if now - last < delay', () => {
      const fn = jest.fn();
      const throttledFn = throttle(fn, 1000);
      const getTimeSpy = jest.spyOn(Date.prototype, 'getTime').mockReturnValue(100);
      throttledFn('param');
      expect(fn).not.toBeCalled();
      expect(getTimeSpy).toBeCalledTimes(1);
    });
  });
});

debounce.js

export const debounce = (fn, delay) => {
  let timeoutID;
  return function (...args) {
    if (timeoutID) {
      clearTimeout(timeoutID);
    }
    timeoutID = setTimeout(() => {
      fn(...args);
    }, delay);
  };
};

debounce.test.js

import { debounce } from './debounce';

jest.useFakeTimers();

describe('65593662', () => {
  describe('debounce', () => {
    it('should call function if timeoutID is undefined', () => {
      const fn = jest.fn();

      const debouncedFn = debounce(fn, 1000);
      debouncedFn('param');
      jest.advanceTimersByTime(1000);
      expect(fn).toBeCalledWith('param');
      expect(fn).toBeCalledTimes(1);
    });

    it('should not call function once if timeoutID exists', () => {
      const fn = jest.fn();

      const debouncedFn = debounce(fn, 1000);
      debouncedFn('param');
      debouncedFn('param');
      debouncedFn('param');
      jest.runAllTimers();
      expect(fn).toBeCalledWith('param');
      expect(fn).toBeCalledTimes(1);
    });
  });
});

单元测试结果:

 PASS  examples/65593662/throttle.test.ts
  65593662
    throttle
      ✓ should call function if now - last > delay (3 ms)
      ✓ should call function if now - last = delay
      ✓ should not call function if now - last < delay (1 ms)

 PASS  examples/65593662/debounce.test.ts
  65593662
    debounce
      ✓ should call function if timeoutID is undefined (5 ms)
      ✓ should not call function once if timeoutID exists (1 ms)

-------------|---------|----------|---------|---------|-------------------
File         | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------|---------|----------|---------|---------|-------------------
All files    |     100 |      100 |     100 |     100 |                   
 debounce.ts |     100 |      100 |     100 |     100 |                   
 throttle.ts |     100 |      100 |     100 |     100 |                   
-------------|---------|----------|---------|---------|-------------------
Test Suites: 2 passed, 2 total
Tests:       5 passed, 5 total
Snapshots:   0 total
Time:        4.168 s

推荐阅读