2016-01-03 4 views
10

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

При разработке я также запускаю свой 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) 

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

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. Я проверил трижды, и нет, где я восстанавливаю песочницу или любые заглушки.

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

+0

Является ли поведение детерминированной?Вы пишете, что проблемы возникают для «некоторых тестов» - всегда ли это один и тот же набор тестов, которые терпят неудачу или какой-то тест может пройти в одном тестовом прогоне, а другой - в другом? –

+0

@TomasKulich он не терпит неудачу в одном и том же тесте каждый раз, но он не работает в определенном подмножестве тестов. Это просто потому, что это те тесты, действия которых в конечном итоге приводят к вызовам API. Если я запустил один тестовый файл, который ранее был ошибочным, он прошел бы. Кажется, что это происходит только при запуске всего пакета, по-видимому, из-за того, что методы 'request' каким-то образом становятся нераскрытыми. – Jakemmarsh

ответ

4

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

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

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

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

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

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

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

https://mochajs.org/

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

+0

Если это действительно ваша проблема, вы все равно можете отлаживать ее трудно - omg omg где ад - это callback/Обещаю, что я не жду. Если это так, я напишу несколько полезных трюков, которые вы можете использовать. –

+0

Похоже, вы были правы. Я знал о «сделанном» обратном вызове для асинхронных тестов и думал, что использую его повсюду, но оказалось, что некоторые из моих других тестов также должны быть асинхронными. Фиксирование нескольких сломанных тестов также означало, что я мог удалить эти уродливые глобальные заглушки на 'request' и только заглушить в соответствующих тестах. Благодаря! – Jakemmarsh

+0

Отлично, я рад, что это помогло. Кроме того, помните, что довольно тонкая опция для функции async - это вернуть объект Promise (однако вам нужно правильно связать Promises). –

1

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