it-swarm.com.de

Testen der asynchronen Funktion mit Mokka

Ich möchte eine asynchrone Javascript-Funktion testen, die in node.js ausgeführt wird und eine einfache Anfrage an eine http-API sendet:

const Host = 'localhost';
const PORT = 80;

http = require('http');

var options = {
    Host: Host,
    port: PORT,
    path: '/api/getUser/?userCookieId=26cf7a34c0b91335fbb701f35d118c4c32566bce',
    method: 'GET'
};
doRequest(options, myCallback);

function doRequest(options, callback) {

    var protocol = options.port == 443 ? https : http;
    var req = protocol.request(options, function(res) {

        var output = '';
        res.setEncoding('utf8');

        res.on('data', function(chunk) {
            console.log(chunk);
            output += chunk;
        });

        res.on('error', function(err) {
            throw err;
        });

        res.on('end', function() {
            var dataRes = JSON.parse(output);
            if(res.statusCode != 200) {
                throw new Error('error: ' + res.statusCode);
            } else {
                try {
                    callback(dataRes);                        
                } catch(err) {
                    throw err;
                }
            }
        });

    });

    req.on('error', function(err) {
        throw err;
    });

    req.end();

}

function myCallback(dataRes) {
    console.log(dataRes);
}

Wenn dieser Code ausgeführt wird, funktioniert er und die Antwort wird wie erwartet angezeigt.

Wenn ich das in einem Mokka-Test durchführe, wird die Anfrage nicht ausgeführt:

describe('api', function() {
    it('should load a user', function() {
        assert.doesNotThrow(function() {
            doRequest(options, myCallback, function(err) {
                if (err) throw err;
                done();
            });
        });
        assert.equal(res, '{Object ... }');
    });
});

Das Problem ist, dass kein Code nach:

var req = protocol.request(options, function(res) {

wird nicht einmal eine einfache console.log ausgeführt.

Kann jemand helfen?

27
MadFool

Sie müssen den Rückruf done als Argument für die Funktion angeben, die für mocha bereitgestellt wird - in diesem Fall die Funktion it(). So wie:

describe('api', function() {
    it('should load a user', function(done) { // added "done" as parameter
        assert.doesNotThrow(function() {
            doRequest(options, function(res) {
                assert.equal(res, '{Object ... }'); // will not fail assert.doesNotThrow
                done(); // call "done()" the parameter
            }, function(err) {
                if (err) throw err; // will fail the assert.doesNotThrow
                done(); // call "done()" the parameter
            });
        });
    });
});

Die Signatur von doRequest(options, callback) gibt zwei Argumente an, wenn Sie jedoch im Test drei angeben. 

Mocha konnte die Methode doRequest(arg1,arg2,arg3) wahrscheinlich nicht finden. 

Hat es keine Fehlerausgabe gegeben? Vielleicht können Sie die Mokka-Optionen ändern, um weitere Informationen zu erhalten.

BEARBEITEN:

undho hat recht, der zweite Assert würde parallel zu assert.doesNotThrow aufgerufen, während er nur im Erfolgsrückruf aufgerufen werden sollte. 

Ich habe den Beispielcode korrigiert.

EDIT 2:

Oder um die Fehlerbehandlung zu vereinfachen (siehe Kommentar von Dan M.):

describe('api', function() {
    it('should load a user', function(done) { // added "done" as parameter
        assert.doesNotThrow(function() {
            doRequest(options, function(res) {
                assert.equal(res, '{Object ... }'); // will not fail assert.doesNotThrow
                done(); // call "done()" the parameter
            }, done);
        });
    });
});
45
Risadinha

Wenn Sie über eine asynchrone Funktion verfügen, die keine Callbacks unterstützt, oder wenn Sie denken, dass die Verwendung unnötiger Callbacks ... nicht erforderlich ist, können Sie den Test auch in einen asynchronen Test umwandeln.

anstatt:

it('should be able to do something', function () {});

einfach machen:

it('should be able to do something', async function () {});
                                     ^^^^^

Jetzt können Sie await async-Funktionen ausführen:

it('should be able to do something', async function () {
  this.timeout(40000);

  var result = await someComplexFunction();

  assert.isBelow(result, 3);
});
2
Thomas W

Ich habe in meinem Projekt einen sehr ähnlichen Test für einen http-Client durchgeführt. Ich füge den Code hier ein und hoffe, das ist nützlich. Hier ist der Client (mein nodejs-Server verwendet express und ich verspreche die Fehlerbehandlung):

var http = require('http');
var querystring = require('querystring');

module.exports = {
  get: function(action, params, res, callback) {
    doPromiseRequest(action, querystring.stringify(params), callback, 'GET', 'application/json')
      .then((response) => callback(response))
      .catch((error) => {
        res.status(500);
        res.render('error', {layout: false, message: error.message, code: 500});
      });
  },
}

function doPromiseRequest(action, params, callback, method, contentType) {
    var options = {
      hostname: 'localhost',
      port: 3000,
      path: '/api/v1/' + action.toString(),
      method: method,
      headers: {
        'Content-Type': contentType,
        'Content-Length': Buffer.byteLength(params)
      }
    };

    return new Promise( (resolve, reject) => {

      var req = http.request(options, 
        function(response) {
          response.setEncoding('utf8');

          var data = '';
          response.on('data', function(chunk) {
            data += chunk;
          });

          response.on('end', function() {
            var parsedResponse;

            try {
              parsedResponse = JSON.parse(data);
            } catch(err) {
              reject({message: `Invalid response from hurricane for ${action}`});
              return;
            }

            if (parsedResponse.error)
              reject(parsedResponse.error);
            else
              resolve(parsedResponse);
          });

          response.on('error', function(err){
            console.log(err.message);
            reject(err);
          });
        });

      req.on('error', function(err) {
        console.log(err);
        reject({message: err.message});
      });

      req.write(params);
      req.end(); 
    });    
}

Und hier ist der Test:

var http = require('http');
var expect = require('chai').expect;
var sinon = require('sinon');
var PassThrough = require('stream').PassThrough;

describe('Hurricane Client tests', function() {
  before(function() {
    this.request = sinon.stub(http, 'request');
  });

  after(function() {
    http.request.restore();
  });

  it('should convert get result to object', function(done) {
    var expected = { hello: 'world' };
    var response = new PassThrough();
    response.statusCode = 200;
    response.headers = {}
    response.write(JSON.stringify(expected));
    response.end();

    var request = new PassThrough();

    this.request.callsArgWith(1, response).returns(request);

    client.get('any', {}, null, function(result) {
      expect(result).to.eql(expected);
      done();
    });
  });
});
0
Antonio Ganci