使用 mocha 和 chai 单元测试 nodejs 模块

如何解决使用 mocha 和 chai 单元测试 nodejs 模块

我有一个 nodejs 应用程序,我正在为其编写单元测试,我有以下情况:我有一个 config.js 文件,我在其中读取多个参数作为环境变量和一些证书以连接到 RabbitMQ。我将此配置导出为模块。

我想要做的是创建一个单元测试文件,该文件将在不同场景下测试模块并尝试实现非常高的覆盖率。

我有多个环境,如 LOCAL_DEV、DEV、UAT 和 PROD,其中包含多个 RabbitMQ 实例。例如,我使用密钥文件连接到 RabbitMQ,在 LOCAL_DEV 中仅使用用户名和密码,在 UAT 中使用 pfx 等等。

诸如 process.env.ENO_ENV 之类的环境变量在部署过程中被注入,其他配置参数我通过 .env_dev 或 .env_uat 文件获得它们。我在进行部署时也会注入证书,并且在“.env_”文件中给出了它们的路径。

例如,在 DEV 中,我有 process.env.ENO_ENV = dev 并且 .env_dev 看起来像:

RABBITMQ_SSL_KEYFILE = 'tls/dev/rmq-XXX.key'
RABBITMQ_SSL_CERTFILE = 'tls/dev/rmq-XXX.crt'
RABBITMQ_SSL_CAFILE = 'tls/dev/rmq-dev-ca.crt'

测试是在 Gitlab 中完成的,我没有任何证书文件或 .env_ 文件,所以我需要模拟 process.env.ENO_ENV 和 .env_file。

config.js 文件内容

const fs = require('fs');
const path = require('path');

// Load config for the set env
global.appRoot = path.resolve(__dirname);
const eno_env = process.env.ENO_ENV || "";

if (process.env.ENO_ENV == 'dev' || 
  process.env.ENO_ENV == 'local_dev' || 
  process.env.ENO_ENV == 'uat' || 
  process.env.ENO_ENV == 'prod') {    
  require('dotenv').config({ path: path.join(global.appRoot,'.env_' + eno_env) });
} else {
  require('dotenv').config({ path: path.join(global.appRoot,'.env') });
}

const {
  PORT,RABBITMQ_PROTOCOL,RABBITMQ_USER,RABBITMQ_PASS,RABBITMQ_HOSTS,RABBITMQ_PORT,RABBITMQ_VHOST,RABBITMQ_EXCHANGE,RABBITMQ_QUEUE,RABBITMQ_SSL_CAFILE,RABBITMQ_SSL_KEYFILE,RABBITMQ_SSL_CERTFILE,RABBITMQ_SSL_PFX,RABBITMQ_SSL_PASSWORD,} = process.env;

let opts = {};

if (RABBITMQ_SSL_PFX) {
  console.log ('RABBITMQ_SSL_PFX : ' + path.join(global.appRoot,RABBITMQ_SSL_PFX));
  opts = {
    pfx: fs.readFileSync(path.join(global.appRoot,RABBITMQ_SSL_PFX)),passphrase: RABBITMQ_SSL_PASSWORD,ca: [fs.readFileSync(path.join(global.appRoot,RABBITMQ_SSL_CAFILE))],rejectUnauthorized: false,};
} else if (RABBITMQ_SSL_KEYFILE) {
  opts = {
    key: fs.readFileSync(path.join(global.appRoot,RABBITMQ_SSL_KEYFILE)),cert: fs.readFileSync(path.join(global.appRoot,RABBITMQ_SSL_CERTFILE)),};
}

const config = {
  app: {
    port: parseInt(PORT) || 8126,enoenv: eno_env,},rabbitmq: {
    protocol: RABBITMQ_PROTOCOL || 'amqps',user: RABBITMQ_USER || 'test',pass: RABBITMQ_PASS || 'test',hosts: RABBITMQ_HOSTS || 'localhost',port: parseInt(RABBITMQ_PORT) || 5671,vhost: RABBITMQ_VHOST || 'test_virtualhost',exchange: RABBITMQ_EXCHANGE || 'test_exchange',queue: RABBITMQ_QUEUE || 'test_queue',opts,};

module.exports = config;

我的问题是通过使用名为 configCert1UnitTest.js 的文件来实现 mocha 和 chai 的单元测试覆盖率:

const should = require('chai').should();

process.env['RABBITMQ_SSL_PFX'] = '/test/test.json';
process.env['RABBITMQ_SSL_PASSWORD'] = 'test';
process.env['RABBITMQ_SSL_CAFILE'] = '/test/test.json';

const config = require('./../config');

describe('env 1',function () {
    // running tests
    it('reads env RABBITMQ_SSL_PFX property,config.rabbitmq.opts.pfx and test.json file correctly',function () {
        try {
            //target = fs.readFileSync(path.join(global.appRoot,process.env['RABBITMQ_SSL_PFX']));
            should.equal( 
                config.rabbitmq.opts.pfx.toString('utf8'),'{\r\n    "property" : "test"\r\n}');
          } catch(e) {
            throw new Error("error reading env RABBITMQ_SSL_PFX property,config.rabbitmq.opts.pfx or test.json: " + e.message);
          }    
    });

    it('reads env RABBITMQ_SSL_CAFILE property,config.rabbitmq.opts.ca and test.json file correctly',function () {
        try {
            should.equal( 
                config.rabbitmq.opts.ca.toString('utf8'),'{\r\n    "property" : "test"\r\n}');
          } catch(e) {
            throw new Error("error reading env RABBITMQ_SSL_CAFILE property,config.rabbitmq.opts.ca or test.json: " + e.message);
          }
              
    });
  });
  
  delete process.env.RABBITMQ_SSL_PFX;
  delete process.env.RABBITMQ_SSL_PASSWORD;
  delete process.env.RABBITMQ_SSL_CAFILE;

现在,为了解决 condif.js 中的另一个 IF 被覆盖的情况,我在文件 configCert2UnitTest.js 中编写了第二个测试用例:

const should = require('chai').should();

process.env['RABBITMQ_SSL_KEYFILE'] = '/test/test.json';
process.env['RABBITMQ_SSL_CERTFILE'] = '/test/test.json';
process.env['RABBITMQ_SSL_CAFILE'] = '/test/test.json';

const config = require('./../config');
  
describe('env 2',function () {
    
    // running tests
    it('reads env RABBITMQ_SSL_KEYFILE property,config.rabbitmq.opts.key and test.json file correctly',function () {
        try {
            should.equal( 
                config.rabbitmq.opts.key.toString('utf8'),'{\r\n    "property" : "test"\r\n}');
          } catch(e) {
            throw new Error("error reading env RABBITMQ_SSL_KEYFILE property,config.rabbitmq.opts.key or test.json: " + e.message);
        }    
    });

    it('reads env RABBITMQ_SSL_CERTFILE property,config.rabbitmq.opts.cert and test.json file correctly',function () {
        try {
            should.equal( 
                config.rabbitmq.opts.cert.toString('utf8'),'{\r\n    "property" : "test"\r\n}');
          } catch(e) {
            throw new Error('error reading env RABBITMQ_SSL_CERTFILE property,config.rabbitmq.opts.cert or test.json:' + e.message);
          }    
    });

    it('reads env RABBITMQ_SSL_CAFILE property,'{\r\n    "property" : "test"\r\n}');
          } catch(e) {
            throw new Error('error reading env RABBITMQ_SSL_CAFILE property,config.rabbitmq.opts.ca or test.json: ' + e.message);
          }    
    });

  });

delete  process.env.RABBITMQ_SSL_KEYFILE;
delete process.env.RABBITMQ_SSL_CERTFILE;
delete process.env.RABBITMQ_SSL_CAFILE;

问题是测试没有涵盖模块配置的 ELSE。

用于在我的 package.json 中进行测试的命令:

...
"test": "npx nyc mocha test --exit --timeout 10000 --reporter mocha-junit-reporter",...

输出

PS D:\zzz-api> npx mocha test
global.appRoot : D:\zzz-liveprice-api
RABBITMQ_SSL_PFX : D:\zzz-api\test\test.json


  env 1
    ✔ reads env RABBITMQ_SSL_PFX property,config.rabbitmq.opts.pfx and test.json file correctly
    ✔ reads env RABBITMQ_SSL_CAFILE property,config.rabbitmq.opts.ca and test.json file correctly

  env 2
    1) reads env RABBITMQ_SSL_KEYFILE property,config.rabbitmq.opts.key and test.json file correctly
    2) reads env RABBITMQ_SSL_CERTFILE property,config.rabbitmq.opts.cert and test.json file correctly
    ✔ reads env RABBITMQ_SSL_CAFILE property,config.rabbitmq.opts.ca and test.json file correctly

  Dummy Test
    ✔ First test that should pass

  Bindings Unit Test
    ✔ Should return true if the correct bindings are found for the EUA
    ✔ Should return true if the correct bindings are found for the POWER
    ✔ Should return true if the correct bindings are found for the GAS


  7 passing (534ms)
  2 failing

  1) env 2
       reads env RABBITMQ_SSL_KEYFILE property,config.rabbitmq.opts.key and test.json file correctly:
     Error: error reading env RABBITMQ_SSL_KEYFILE property,config.rabbitmq.opts.key or test.json: Cannot read property 'toString' of undefined
      at Context.<anonymous> (test\configCert2UnitTest.js:19:19)
      at processImmediate (internal/timers.js:439:21)

  2) env 2
       reads env RABBITMQ_SSL_CERTFILE property,config.rabbitmq.opts.cert and test.json file correctly:
     Error: error reading env RABBITMQ_SSL_CERTFILE property,config.rabbitmq.opts.cert or test.json:Cannot read property 'toString' of undefined
      at Context.<anonymous> (test\configCert2UnitTest.js:30:19)
      at processImmediate (internal/timers.js:439:21)

我忘了提到我在我的 \test 目录中为测试设置了一个 test.json 文件,其中包含 '{\r\n "property" : "test"\r\n}' 只是为了模拟证书,在 Gitlab 中测试时缺少这些证书。

任何想法如何解决这个问题?谢谢!

解决方法

好的,我通过执行以下操作设法解决了我的问题:

我在 config.js 中创建了一个函数:

const fs = require('fs');
const path = require('path');

module.exports = () => {
  // Load config for the set env
  global.appRoot = path.resolve(__dirname);
  const eno_env = process.env.ENO_ENV || "";
  ...
  return config;
}

然后,在我的测试文件 configUnitTest.js 中,我将常量定义移到 describe() 方法中,并将其放在 before() 块中,而不是在测试文件的开头使用它。

此外,mocha 会测试文件夹 test 中的其余文件,如果有另一个读取 const conf = require('../config')(); 的测试文件,这将

const should = require('chai').should();
const mock = require('mock-fs');
const path = require('path');

describe('Reading config.js - environmental vars 1',function () {
  let conf;
  before('mocking files and process.env vars',function () {        
    ...
    process.env['ENO_ENV'] = 'local_dev';
    conf = require('../config')(); // reads process.env.ENO_ENV
  });
  ...

  // running tests  
  it('TRUE if it reads var process.env.RABBITMQ_SSL_PFX and mocked file test.json',function () {
    ...
  });
  ...
});

describe('Reading config.js - environmental vars 2',function () {        
    ...
    conf = require('../config')(); // reads const eno_env = process.env.ENO_ENV || "";
  });
  ...

  // running tests  
  it('TRUE if it reads var process.env.RABBITMQ_SSL_PFX and mocked file test.json',function () {
    ...
  });
  ...
});

我希望它可以帮助其他人。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?