为第三方 cli 包编写单元测试用例

问题描述

我有一个使用 yargs 构建的基本 CLI 程序。我能够涵盖应用程序中导出函数的测试用例。

正如您在下面看到的那样,测试覆盖率不是从第 12-18 行开始的。我们如何为 yargs 等第三方包编写单元测试覆盖率?

index.js

const yargs = require('yargs');
const { hideBin } = require('yargs/helpers');
const greet = (name) => {
  return `Welcome ${name}`;
};
yargs(hideBin(process.argv)).command(
  'run [name]','print name',(yargs) => {
    yargs.positional('name',{ describe: 'Your name',type: 'string' });
  },(args) => {
    const { name } = args;

    const greetMsg = greet(name);

    console.log(greetMsg);
  }
).argv;

module.exports = { greet };

index.test.js

const { greet } = require('./index')

describe.only('greeting',() => {
  it('greet',async () => {
    const greetMsg = greet('test')

    expect(greetMsg).toBe('Welcome test')
  })
})

测试覆盖率

PASS  ./index.test.js
greeting
  ✓ greet (5 ms)

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |   63.64 |      100 |   33.33 |   63.64 |                   
index.js  |   63.64 |      100 |   33.33 |   63.64 | 12-18            
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed,1 total
Tests:       1 passed,1 total
Snapshots:   0 total
Time:        1.316 s,estimated 2 s
Ran all test suites.

解决方法

您可以使用 jest.doMock(moduleName,factory,options) 来模拟 yargsyargs/helpers 模块。由于模块作用域中的 yargs 函数将在需要模块时执行。您需要使用 jest.resetModules() 来重置模块注册表 - 在需要 index.js 模块之前,每个测试用例的所有必需模块的缓存。

这对于隔离本地状态可能在测试之间发生冲突的模块很有用。

例如

index.js

const yargs = require('yargs');
const { hideBin } = require('yargs/helpers');

const greet = (name) => {
  return `Welcome ${name}`;
};
yargs(hideBin(process.argv)).command(
  'run [name]','print name',(yargs) => {
    yargs.positional('name',{ describe: 'Your name',type: 'string' });
  },(args) => {
    const { name } = args;

    const greetMsg = greet(name);

    console.log(greetMsg);
  }
).argv;

module.exports = { greet };

index.test.js

describe.only('greeting',() => {
  beforeEach(() => {
    jest.resetModules();
  });
  it('greet',() => {
    const { greet } = require('./index');
    const greetMsg = greet('test');
    expect(greetMsg).toBe('Welcome test');
  });

  it('should pass',() => {
    jest.doMock('yargs');
    jest.doMock('yargs/helpers');
    const yargs = require('yargs');
    const { hideBin } = require('yargs/helpers');
    const mArgv = {
      command: jest.fn().mockImplementation(function (command,description,builder,handler) {
        builder(this);
        handler({ name: 'teresa teng' });
        return this;
      }),argv: {},positional: jest.fn(),};
    yargs.mockReturnValueOnce(mArgv);
    require('./');
    expect(hideBin).toBeCalled();
    expect(yargs).toBeCalled();
    expect(mArgv.positional).toBeCalledWith('name',type: 'string' });
  });
});

测试结果:

 PASS  examples/67830954/index.test.js (7.17 s)
  greeting
    ✓ greet (355 ms)
    ✓ should pass (23 ms)

  console.log
    Welcome teresa teng

      at examples/67830954/index.js:18:13

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |     100 |      100 |     100 |     100 |                   
 index.js |     100 |      100 |     100 |     100 |                   
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed,1 total
Tests:       2 passed,2 total
Snapshots:   0 total
Time:        7.668 s,estimated 8 s