如何为Inquirer.js编写单元测试?

8
我在想如何为npm包Inquirer.js编写单元测试,它是一个使CLI包更易于使用的工具。我已经阅读了这篇文章,但我无法让其运行。

以下是需要测试的代码:

const questions = [
                {
                    type: 'input',
                    name: 'email',
                    message: "What's your email ?",
                },
                {
                    type: 'password',
                    name: 'password',
                    message: 'Enter your password (it will not be saved neither communicate for other purpose than archiving)'
                }
            ];

            inquirer.prompt(questions).then(answers => {
                const user = create_user(answers.email, answers.password);
                let guessing = guess_unix_login(user);
                guessing.then(function (user) {
                    resolve(user);
                }).catch(function (message) {
                    reject(message);
                });
            } );

...这是使用Mocha编写的测试:

describe('#create_from_stdin', function () {
            this.timeout(10000);
            check_env(['TEST_EXPECTED_UNIX_LOGIN']);
            it('should find the unix_login user and create a complete profile from stdin, as a good cli program', function (done) {
                const user_expected = {
                    "login": process.env.TEST_LOGIN,
                    "pass_or_auth": process.env.TEST_PASS_OR_AUTH,
                    "unix_login": process.env.TEST_EXPECTED_UNIX_LOGIN
                };
                let factory = new profiler();
                let producing = factory.create();
                producing.then(function (result) {
                    if (JSON.stringify(result) === JSON.stringify(user_expected))
                        done();
                    else
                        done("You have successfully create a user from stdin, but not the one expected by TEST_EXPECTED_UNIX_LOGIN");
                }).catch(function (error) {
                    done(error);
                });
            });
        });

我希望使用process.env.TEST_LOGIN(回答第一个Inquirer.js问题)和process.env.TEST_PASS_OR_AUTH(回答第二个Inquirer.js问题)填充stdin,以查看函数是否创建了有效的配置文件(使用工厂对象的create方法猜测unix_login的值)。
我试图理解Inquirer.js如何对自身进行单元测试,但我对NodeJS的理解不够好。你能帮我完成这个单元测试吗?
2个回答

11

如果您不想测试某些功能,可以简单地模拟或存根它们。

  • module.js - 要测试的模块的简化示例

const inquirer = require('inquirer')

module.exports = (questions) => {
  return inquirer.prompt(questions).then(...)
}
  • module.test.js

    const inquirer = require('inquirer')
    const module = require('./module.js')
    
    describe('test user input' () => {
    
      // stub inquirer
      let backup;
      before(() => {
        backup = inquirer.prompt;
        inquirer.prompt = (questions) => Promise.resolve({email: 'test'})
      })
    
      it('should equal test', () => {
        module(...).then(answers => answers.email.should.equal('test'))
      })
    
      // restore
      after(() => {
        inquirer.prompt = backup
      })
    
    })
    
  • 有用于模拟/存根的库,例如sinon

    在这种情况下,模拟inquirer.prompt更容易,因为.prompt只是主导出项inquirer上的属性,它将在module.jsmodule.test.js中引用相同的对象。对于更复杂的情况,可以使用类似proxyquire的库来帮助处理。或者您可以以有助于轻松切换依赖项进行测试的方式创建模块。例如:

    • module.js - 将其制作成“工厂”函数,该函数返回已注入依赖项的主要函数,这些依赖项可以自动(通过默认参数)或手动注入。

    module.exports = ({
      inquirer = require('inquirer'),
    } = {}) => (questions) => {
      return inquirer.prompt(questions).then(...)
    }
    
  • module.test.js

    const module = require('./module.js')
    
    describe('test user input' () => {
    
      const inquirer = {prompt: () => Promise.resolve({email: 'test'})};
    
      it('should equal test', () => {
        module({inquirer})(...).then(answers => answers.email.should.equal('test'))
      })
    })
    

  • 谢谢您的解释,但我可能没有完全理解。我希望能够只填充 stdin 一些数据,inquirer.js 将读取这些数据。以下是我通常测试标准命令行程序的方式:echo "test" | node index.js。"index.js" 文件对应于此代码片段:https://nodejs.org/dist/latest-v8.x/docs/api/readline.html#readline_readline它会产生以下预期输出:OHAI> test Say what? I might have heard 'test' OHAI> Have a great day! - Oscar
    如果你不使用模拟库,那么你本质上就是在重新测试这些库本身。这对于集成测试可能是有好处的,但是你的问题是关于单元测试的,而最佳实践是只需模拟/存根依赖项。 - laggingreflex
    好的,你的建议是信任inquirer.js并且只测试我的代码对吗?(但是从我理解你的代码来看,我需要更改我的模块代码而不仅仅是我的测试对吗?) - Oscar
    我的建议是在单元测试中模拟库。这更多地涉及隔离你正在进行单元测试的内容。至于代码中需要的更改,我仅使用了一个简化的示例(与您发布的代码相关),以演示如何进行模拟和存根。在我的第一个示例中,您根本不需要更改模块代码,但我已经列出了它所带来的注意事项,并提供了两个解决方案(即,库proxyquire和“依赖注入”模式(请随意研究它们并决定哪种适合您))。 - laggingreflex

    2

    使用 inquirer.jsjest 测试框架

    1. 模拟 inquirer
    2. 用响应模拟 .prompt

    module-test.js

    import module from './module';
    import inquirer from 'inquirer';
    
    jest.mock('inquirer');
    
    describe('Module test', () => {
      test('user input', async () => {
        expect.assertions(1);
        inquirer.prompt = jest.fn().mockResolvedValue({ email: 'some@example.com' });
    
        await expect(module()).resolves.toEqual({ email: 'some@example.com' });
      });
    });
    

    (使用 ES6 或 TypeScript 语法。)

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接