Какова цель функции Mocha before()?
В Mocha есть несколько "крючков" для запуска вспомогательных функций в тесте отдельно от самих тестовых случаев (очистка баз данных, создание файлов-макетов и т.д.).
Однако, в случае before()
( не beforeEach()
, тот, который я получаю), он кажется довольно избыточным.
before()
запускает логику один раз перед всеми тестами в текущем пакете, поэтому зачем мне даже переносить ее в функцию?
Нет различий между двумя следующими:
describe('Something', function() {
before(doSomePreTestLogic);
//Tests ahoy
});
и
describe('Something', function() {
doSomePreTestLogic();
//Tests ahoy
});
Какой смысл обертывать мою тестовую логику с помощью before()
?
Ответы
Ответ 1
Есть несколько разных причин, по которым вы можете выбрать крючок before()
в своих тестах:
Семантика
Это, наверное, самый большой. Иногда вам может потребоваться выполнить задачу, например, заполнить некоторые фиктивные данные в вашей базе данных, прежде чем запускать фактический тест. Конечно, вы можете это сделать в самом тесте, но это отбрасывает вашу отчетность, если вы сталкиваетесь с ошибкой во время предварительного заполнения и перед запуском фактического тестового примера. Имея крючок before()
, у вас есть логическое, семантически допустимое место для вставки такого типа логики.
Очиститель для асинхронных тестов
Точно так же, как тесты мокки могут быть асинхронными, так что ваша логика крюка before()
. Возвращаясь к предпопуляционному примеру, это удобно, так как это означает, что вся ваша логика предварительного тестирования асинхронизации не приведет к еще одному уровню отступов во всей вашей фактической логике тестирования.
Совместимость с другими крючками:
Mocha также предоставляет несколько других крючков, а именно: after()
, beforeEach()
и afterEach()
. Вероятно, вы могли бы задать этот же вопрос и обо всех этих других крючках, но если вы поймете, что у любого из них есть обоснованное существование, то before()
действительно нужно включить для округления API.
Ответ 2
Upshot
Поместите прямо внутри кода обратного вызова describe
, который будет создавать набор тестов. Я говорю о вызовах it
, а также о функциях, которые могут циклически перебирать таблицы или файлы, чтобы объявить кучу тестов (вызывая it
в цикле).
Поместите внутри крючки код, который фактически инициализирует состояние, на которое зависят тесты.
Все остальные соображения довольно вторичны.
Позвольте мне объяснить...
Фон
Mocha выполняет тестовый набор в две фазы:
-
Он обнаруживает, какие тесты существуют. На этом этапе он будет немедленно выполнять обратные вызовы, переданные в describe
, и записывать для будущего вызова обратные вызовы, переданные функциям, объявляющим тесты (it
и тому подобное), и функции, объявляющие крючки (before
, beforeEach
, after
и т.д.).
-
Он запускает тесты. На этом этапе будут выполняться обратные вызовы, которые были записаны ранее.
Разница
Итак, рассмотрим этот пример:
function dump () { console.log("running:", this.test.fullTitle()); }
describe("top", function () {
before(dump);
it("test 1", dump);
it("test 2", dump);
describe("level 1", function () {
before(dump);
it("test 1", dump);
it("test 2", dump);
});
});
Обратите внимание, что fullTitle
дает полное имя теста, начиная с верхнего уровня describe
, проходя через любой вложенный describe
до it
или hook, который содержит этот вызов. Запустите с репортером spec
и сохраните только строки running:
, вы получите:
running: top "before all" hook: dump
running: top test 1
running: top test 2
running: top level 1 "before all" hook: dump
running: top level 1 test 1
running: top level 1 test 2
Обратите внимание на порядок крючков и как каждый выполняется непосредственно перед тестами, объявленными в соответствующем обратном вызове describe
.
Рассмотрим этот набор:
function dump () { console.log("running:", this.test.fullTitle()); }
function directDump() { console.log("running (direct):", this.fullTitle()); }
describe("top", function () {
directDump.call(this);
it("test 1", dump);
it("test 2", dump);
describe("level 1", function () {
directDump.call(this);
it("test 1", dump);
it("test 2", dump);
});
});
Запустите с репортером spec
и сохраните только строки running:
, вы получите:
running (direct): top
running (direct): top level 1
running: top test 1
running: top test 2
running: top level 1 test 1
running: top level 1 test 2
Обратите внимание, что оба вызова directDump
выполняются раньше всего.
Последствия
-
Если какой-либо код инициализации, который вы помещаете непосредственно внутри обратного вызова на describe
, терпит неудачу, весь пробег завершается с ошибкой. Никакой тест не будет выполнен. Конец истории.
-
Если какой-либо код инициализации, который вы помещаете внутри крюка before
, терпит неудачу, последствия содержатся.. С одной стороны, поскольку крюк before
запускается в данный момент необходимо, есть возможность запуска любых тестов, которые запланированы ранее. Кроме того, Mocha будет пропускать те тесты, которые зависят от крюка before
. Например, допустим, что этот набор:
function dump () { console.log("running:", this.test.fullTitle()); }
describe("top", function () {
before(dump);
it("test 1", dump);
it("test 2", dump);
describe("level 1", function () {
before(function () { throw new Error("foo"); });
it("test 1", dump);
it("test 2", dump);
});
describe("level 1 (second)", function () {
before(dump);
it("test 1", dump);
it("test 2", dump);
});
});
Если вы запустите его с репортером spec
, весь вывод (минус трассировка стека) будет выглядеть примерно так:
top
running: top "before all" hook: dump
running: top test 1
✓ test 1
running: top test 2
✓ test 2
level 1
1) "before all" hook
level 1 (second)
running: top level 1 (second) "before all" hook: dump
running: top level 1 (second) test 1
✓ test 1
running: top level 1 (second) test 2
✓ test 2
4 passing (5ms)
1 failing
1) top level 1 "before all" hook:
Error: foo
[stack trace]
Обратите внимание, как: а) некоторые тесты выполнялись до неудачного крючка и б) Mocha все еще выполнял тесты, которые не зависят от крючков.
Ответ 3
Семантика, как на машинах, так и на человеческом уровне.
Кроме того, он поддерживает тестовый код в соответствии с интерфейсом "экспорт", например,
module.exports = {
before: function(){
// ...
},
'Array': {
'#indexOf()': {
'should return -1 when not present': function(){
[1,2,3].indexOf(4).should.equal(-1);
}
}
}
};