Подтвердить что ты не робот

Тестирование асинхронной функции с мокко

Я хочу проверить асинхронную функцию javascript, которая работает в node.js, и делает простой запрос на http api:

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);
}

Выполняется этот код, и ответ будет отображаться как ожидалось.

Если я выполняю это в мокко-тесте, запрос не выполняется:

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 ... }');
    });
});

Проблема в том, что никакого кода после:

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

выполняется даже не простая консоль .log.

Помогает ли кто-нибудь?

4b9b3361

Ответ 1

Вы должны указать обратный вызов done как аргумент функции, которая предоставляется мокко - в этом случае функция it(). Например:

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
            });
        });
    });
});

Кроме того, подпись doRequest(options, callback) указывает два аргумента, хотя, когда вы вызываете это в тесте, вы предоставляете три.

Мокко, вероятно, не смог найти метод doRequest(arg1,arg2,arg3).

Не выдал ли какой-нибудь результат ошибки? Возможно, вы можете изменить параметры мокко, чтобы получить дополнительную информацию.

EDIT:

andho правильно, второе утверждение будет вызываться параллельно с assert.doesNotThrow, а его нужно вызывать только в обратном вызове.

Я установил код примера.

ИЗМЕНИТЬ 2:

Или, чтобы упростить обработку ошибок (см. комментарий 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);
        });
    });
});

Ответ 2

Я сделал очень похожий тест в моем проекте для http-клиента. Я вставляю код здесь и надеюсь, что это полезно. Вот клиент (мой сервер nodejs использует экспресс, и я использую обещание для обработки ошибок):

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(); 
    });    
}

И вот тест:

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();
    });
  });
});

Ответ 3

Если у вас есть асинхронная функция, которая не поддерживает обратные вызовы, или если вы считаете, что использование ненужных обратных вызовов... ненужно, вы также можете просто включить тест в асинхронный тест.

вместо:

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

просто выполните:

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

Теперь вы можете await асинхронные функции:

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

  var result = await someComplexFunction();

  assert.isBelow(results, 3);
});