Ошибка тестового набора Mocha при попытке подключения к API

Я запускаю свой тестовый пакет, используя mocha, через gulp-jsx-coverage и gulp-mocha. Все мои тесты выполняются и проходят/сбой, как и ожидалось. Тем не менее, некоторые из моих тестируемых модулей делают HTTP-запросы к моему API через библиотеку superagent.

При разработке я также запускаю свой API на localhost:3000 рядом с моим клиентским приложением, и поэтому это URL-адрес, который пытается получить мой клиентский тест. Однако при тестировании API обычно не работает. Это приводит к следующей ошибке при каждом прохождении запроса:

Error in plugin 'gulp-mocha'
Message:
    connect ECONNREFUSED
Details:
    code: ECONNREFUSED
    errno: ECONNREFUSED
    syscall: connect
    domainEmitter: [object Object]
    domain: [object Object]
    domainThrown: false
Stack:
Error: connect ECONNREFUSED
    at exports._errnoException (util.js:746:11)
    at TCPConnectWrap.afterConnect [as oncomplete] (net.js:983:19)

Я попытался усечь все методы в библиотеке superagent (aliased as request) в глобальном помощнике, например:

function httpStub() {
  return {
    withCredentials: () => {
      return { end: () => {} };
    }
  };
};

beforeEach(function() {
  global.sandbox = sinon.sandbox.create();

  global.getStub = global.sandbox.stub(request, 'get', httpStub);
  global.putStub = global.sandbox.stub(request, 'put', httpStub);
  global.patchStub = global.sandbox.stub(request, 'patch', httpStub);
  global.postStub = global.sandbox.stub(request, 'post', httpStub);
  global.delStub = global.sandbox.stub(request, 'del', httpStub);
});

afterEach(function() {
  global.sandbox.restore();
});

Но по какой-то причине, когда некоторые тесты встречаются, методы не затухают, поэтому я достигаю ошибки ECONNREFUSED. Я проверил трижды, и нет, где я восстанавливаю песочницу или любые заглушки.

Есть ли способ исправить проблему, с которой я столкнулся, или более чистое решение для этого?

Ответ 1

Проблема может быть вызвана неправильным выполнением асинхронных действий в вашем тесте. Представьте следующий пример:

it('is BAD asynchronous test', () => {
  do_something()
  do_something_else()
  return do_something_async(/* callback */ () => {
    problematic_call()
  })
})

Когда Mocha находит такой тест, он выполняет (синхронно) do_something, do_something_else и do_something_async. В тот момент, с точки зрения Mochas, тест закончен, Mocha выполняет afterEach() для него (что плохо, problematic_call еще не вызвано!) И (что еще хуже), он начинает следующий тест

Теперь, очевидно, выполнение тестов (и beforeEach и afterEach) параллельно может привести к действительно странным и непредсказуемым результатам, поэтому неудивительно, что что-то получилось неправильным (вероятно, после того, как каждый из них был вызван в середине некоторого теста, который приводит к неустойчивости окружающая среда)

Что с ним делать:

Всегда сигнализируйте Mocha, когда ваш тест заканчивается. Это можно сделать либо путем возврата объекта Promise, либо путем вызова done обратного вызова:

it('is BAD asynchronous test', (done) => {
  do_something()
  do_something_else()
  return do_something_async(/* callback */ () => {
    problematic_call()
    done()
  })
})

https://mochajs.org/

Таким образом, Mocha "знает", когда заканчивается ваш тест, и только затем он запускает следующий тест.

Ответ 2

request уже требуется вашим приложением, поэтому не имеет значения, не запустил ли он его в своих тестах.

Вам нужно использовать что-то вроде rewire и заменить request, требуемую вашим приложением, вашей заглубленной версией.

Что-то вроде этого должно помочь

var request = require('superagent'),
    rewire = require('rewire'),
    sinon = require('sinon'),
    application = rewire('your-app'),
    rewiredRequest;

function httpStub() {
    return {
        withCredentials: () => {
            return { end: () => {} };
        }
    };
};

beforeEach(function() {
    var requestStub = {
        get: httpStub,
        put: httpStub,
        patch: httpStub,
        post: httpStub,
        del: httpStub
    };

    // rewiredRequest is a function that will revert the rewiring
    rewiredRequest = application.__set__({
        request: requestStub
    });
});

afterEach(function() {
  rewiredRequest();
});