简体   繁体   中英

Mocha Unit Testing of Controller resolving promise coming from services

I have controller :

 function(req, res) { // Use the Domain model to find all domain CIO.find(function(err, CIOs) { if (err) { response = responseFormat.create(false, "Error getting CIOs", err, {}); res.status(400).json(response); } else { var metrics = { "count": CIOs.length }; // .then means it will wait for it to finish, then let you have the result var promises = []; for (i in CIOs) { promises.push(Analysis.structureMetrics(CIOs[i].toObject())) } var output = [] var errors = [] Q.allSettled(promises) .then(function(results) { for (i in results) { if (results[i].state === "rejected") { console.log(results[i]) errors.push(results[i].reason.errors) output.push(results[i].reason) } else { output.push(results[i].value) } } }).then(function() { response = responseFormat.create(true, "List of all CIOs", output, metrics, errors); res.status(200).json(response); }) } }); }; 

and cio.test file :

 describe('/cio', function() { describe('GET', function() { //this.timeout(30000); before(function() { }); it('should return response', function(done) { var response = http_mocks.createResponse({eventEmitter: require('events').EventEmitter}) var request = http_mocks.createRequest({ method: 'GET', url: '/cio', }) //var data = JSON.parse( response._getData() ); response.on('end', function() { response.statusCode.should.be.equal(400); done(); }) cioCtrl.getCIOs(request, response); }); }); }); 

getting Error

Error: timeout of 10000ms exceeded. Ensure the done() callback is being called in this test

1>I have already tried increasing the time, but It doesn't work.

2> What I found is response.('end', function(){}) is not getting called, but not sure why

Any help would be appreciated. Thanks!

Very good approach for unit testing is using the dependency injection .

For this, your controller file should be something like this:

module.exports = class MyController {
  constructor(CIO) {
    this._CIO = CIO;
    this.handler = this.handler.bind(this);
  }

  handler(req, res) {
    // your code here using this._CIO
  }
};

Than in your main file, you create instance of controller:

const MyController = require('./controllers/my-controller');

// require your CIO, or create instance...
const CIO = require('./CIO');

const myController = new MyController(CIO);

You simply then pass instance of controller, or it's handler function to the place where it will be used.

Using this approach allows you to test well. Assume your 'it' will look something like this:

it('should work', function(done) {
  const fakeCIO = { 
    find: function() {
      done();
    }
  };
  const myController = new MyController(fakeCIO);
  myController.handler();
});

Basic differences between testing techniques:

  • unit test - you test one single unit, how it calls functions, makes assignments, returns values
  • integration test - you add database to your previous test and check how it is stored/deleted/updated
  • end-to-end test - you add API endpoint to previous integration test and check how whole your flow works

Update:
Using async/await approach you will be able to test more things using your controller. Assume modifying it in something like this:

async function(req, res) {
  try {
    const CIOs = await CIO.find();
    const metrics = {
      "count": CIOs.length
    };
    const promises = CIOs.map(el => Analysis.structureMetrics(el.toObject());

    for(const promise of promises) {
      const result = await promise();
      // do whatever you need with results
    }

  } catch(err) {
    const response = responseFormat.create(false, "Error getting CIOs", err, {});
    res.status(400).json(response);
  }

Using such approach, during unit testing you can also test that your controller calls methods:

  • responseFormat.create
  • Analysis.structureMetrics
  • res.status
  • res.json
  • test catch branch to be executed

All this is done with fake objects. And sure using OOP is not mandatory, it's just a matter of habits, you can do the same using functional style, with closures , for example.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM