簡體   English   中英

使用 mocha 和 chai 單元測試 nodejs 模塊

[英]Unit testing nodejs module using mocha and chai

我有一個 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)),
    ca: [fs.readFileSync(path.join(global.appRoot, RABBITMQ_SSL_CAFILE))],
    rejectUnauthorized: false,
  };
}

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, 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_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 () {
  let conf;
  before('mocking files and process.env vars', 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 () {
    ...
  });
  ...
});

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM