После изменения или добавления кода вам следует запустить существующие модульные тесты и подумать о написании большего. Все тесты выполняются на несжатых версиях кода.
Существует два набора модульных тестов: тесты JS и тесты генератора блоков.
JS-тесты
JS-тесты подтверждают работу внутренних функций JavaScript в ядре Blockly. Мы используем Mocha для запуска модульных тестов, Sinon для заглушки зависимостей и Chai для утверждения кода.
Запуск тестов
Как в блочных, так и в блочных образцах npm run test
запустит модульные тесты. В блочном режиме также будут выполняться другие тесты, такие как анализ и компиляция. Вы также можете открытьtests tests/mocha/index.html
в браузере, чтобы в интерактивном режиме запустить все тесты Mocha.
Написание тестов
Для запуска тестов мы используем интерфейс Mocha TDD. Тесты организованы в наборы, которые могут содержать как дополнительные поднаборы, так и/или тесты. Как правило, каждый компонент Blockly (например, toolbox
или workspace
) имеет собственный тестовый файл, содержащий один или несколько наборов. Каждый пакет может иметь методы setup
и teardown
, которые будут вызываться до и после каждого теста в этом наборе соответственно.
Помощники по тестированию
У нас есть ряд вспомогательных функций, специфичных для Blockly, которые могут быть полезны при написании тестов. Их можно найти в ядре и в блочно-сэмплах .
Вспомогательные функции включают в себя sharedTestSetup
и sharedTestTeardown
, которые необходимо вызывать до и после тестов (см. раздел «Требования»).
sharedTestSetup
:
- Устанавливает поддельные таймеры sinon (в некоторых тестах вам понадобится использовать
this.clock.runAll
). - Заглушка Blockly.Events.fire для немедленного запуска (настраивается).
- Настраивает автоматическую очистку типов блоков, определенных с помощью
defineBlocksWithJsonArray
. - Объявляет несколько свойств в контексте
this
, которые должны быть доступны:-
this.clock
(но его не следует восстанавливать, иначе это вызовет проблемы вsharedTestTeardown
) -
this.eventsFireStub
-
this.sharedCleanup
(для использования сaddMessageToCleanup
иaddBlockTypeToCleanup
) (ПРИМЕЧАНИЕ: вам не нужно использоватьaddBlockTypeToCleanup
если вы определили блок с помощьюdefineBlocksWithJsonArray
)
-
Функция имеет один дополнительный options
для настройки. В настоящее время он используется только для определения того, следует ли заглушить Blockly.Events.fire
для немедленного запуска (по умолчанию заглушка).
sharedTestTeardown
:
- Удаляет рабочую область
this.workspace
(в зависимости от того, где она была определена, дополнительную информацию см. в разделе «Требования к тестированию»). - Восстанавливает все заглушки.
- Очищает все типы блоков, добавленные с помощью
defineBlocksWithJsonArray
иaddBlockTypeToCleanup
. - Очищает все сообщения, добавленные с помощью
addMessageToCleanup
.
Требования к тестированию
- Каждый тест должен вызывать
sharedTestSetup.call(this);
в качестве первой строки в настройке самого внешнего пакетаsharedTestTeardown.call(this);
в качестве последней строки при разборе самого внешнего набора файла. - Если вам нужна рабочая область с универсальным набором инструментов, вы можете использовать один из предустановленных наборов инструментов в тестовом файле
index.html
. См. пример ниже. - Вы должны правильно распорядиться
this.workspace
. В большинстве тестов вы определяетеthis.workspace
во внешнем наборе и используете его для всех последующих тестов, но в некоторых случаях вы можете определить или переопределить его во внутреннем наборе (например, для одного из ваших тестов требуется рабочее пространство с различными параметрами). чем вы изначально установили). По окончании испытания его необходимо утилизировать.- Если вы определяете
this.workspace
в самом внешнем наборе и никогда не переопределяете его, никаких дальнейших действий не требуется. Он будет автоматически удален с помощьюsharedTestTeardown
. - Если вы определяете
this.workspace
впервые во внутреннем наборе (т. е. вы не определили его в самом внешнем наборе), вы должны вручную избавиться от него, вызвавworkspaceTeardown.call(this, this.workspace)
при демонтаже этого люкс. - Если вы определяете
this.workpace
в самом внешнем наборе, но затем переопределяете его во внутреннем наборе тестов, вы должны сначала вызватьworkspaceTeardown.call(this, this.workspace)
прежде чем переопределить его , чтобы удалить исходное рабочее пространство, определенное в наборе верхнего уровня. . Вы также должны вручную удалить новое значение, снова вызвавworkspaceTeardown.call(this, this.workspace)
при разборе этого внутреннего набора.
- Если вы определяете
Структура теста
Модульные тесты обычно следуют заданной структуре, которую можно резюмировать как «упорядочить, действовать, утверждать».
- Упорядочить : настроить состояние мира и все необходимые условия для тестируемого поведения.
- Действие : вызов тестируемого кода, чтобы активировать тестируемое поведение.
- Assert : делайте утверждения о возвращаемом значении или взаимодействии с имитируемыми объектами, чтобы проверить правильность.
В простом тесте может не быть никакого поведения, которое нужно было бы организовать, и этапы действия и утверждения можно объединить, встроив в утверждение вызов тестируемого кода. В более сложных случаях ваши тесты будут более читабельными, если вы будете придерживаться этих трех этапов.
Вот пример тестового файла (упрощенный по сравнению с реальным).
suite('Flyout', function() {
setup(function() {
sharedTestSetup.call(this);
this.toolboxXml = document.getElementById('toolbox-simple');
this.workspace = Blockly.inject('blocklyDiv',
{
toolbox: this.toolboxXml
});
});
teardown(function() {
sharedTestTeardown.call(this);
});
suite('simple flyout', function() {
setup(function() {
this.flyout = this.workspace.getFlyout();
});
test('y is always 0', function() {
// Act and assert stages combined for simple test case
chai.assert.equal(this.flyout.getY(), 0, 'y coordinate in vertical flyout is 0');
});
test('x is right of workspace if flyout at right', function() {
// Arrange
sinon.stub(this.flyout.targetWorkspace, 'getMetrics').returns({
viewWidth: 100,
});
this.flyout.targetWorkspace.toolboxPosition = Blockly.TOOLBOX_AT_RIGHT;
this.flyout.toolboxPosition_ = Blockly.TOOLBOX_AT_RIGHT;
// Act
var x = this.flyout.getX();
// Assert
chai.assert.equal(x, 100, 'x is right of workspace');
});
});
});
Что следует отметить из этого примера:
- Пакет может содержать другие пакеты, которые имеют дополнительные методы
setup
иteardown
. - Каждый набор и тест имеют описательное имя.
- Утверждения Chai используются для создания утверждений о коде.
- Вы можете указать необязательный строковый аргумент, который будет отображаться в случае неудачного завершения теста. Это упрощает отладку неработающих тестов.
- Порядок параметров:
chai.assert.equal(actualValue, expectedValue, optionalMessage)
. Если вы поменяете местамиactual
иexpected
, сообщения об ошибках не будут иметь смысла.
- Sinon используется для заглушки методов, когда вы не хотите вызывать реальный код. В этом примере мы не хотим вызывать функцию реальных показателей, поскольку она не имеет отношения к этому тесту. Нас волнует только то, как результаты используются тестируемым методом. Sinon заглушает функцию
getMetrics
, чтобы вернуть стандартный ответ, который мы можем легко проверить в наших тестовых утверждениях. - Методы
setup
для каждого пакета должны содержать только общие настройки, применимые ко всем тестам. Если тест на конкретное поведение основан на определенном условии, это условие должно быть четко указано в соответствующем тесте.
Отладка тестов
- Вы можете открыть тесты в браузере и использовать инструменты разработчика, чтобы установить точки останова и выяснить, происходят ли ваши тесты неожиданно неудачно (или неожиданно успешно пройдены!).
Установите
.only()
или.skip()
для теста или пакета, чтобы запустить только этот набор тестов, или пропустите тест. Например:suite.only('Workspace', function () { suite('updateToolbox', function () { test('test name', function () { // ... }); test.skip('test I don’t care about', function () { // ... }); }); });
Не забудьте удалить их перед фиксацией кода.
Тесты блочных генераторов
Каждый блок имеет свои модульные тесты. Эти тесты проверяют, что блоки генерируют код, а не функции, как предполагалось.
- Загрузите
tests/generators/index.html
в Firefox или Safari. Обратите внимание, что Chrome и Opera имеют ограничения безопасности, которые не позволяют загружать тесты из локальной системы «file://» (ошибки 41024 и 47416 ). - Выберите соответствующую часть системы для тестирования из раскрывающегося меню и нажмите «Загрузить». Блоки должны появиться в рабочей области.
- Нажмите «JavaScript».
Скопируйте и запустите сгенерированный код в консоли JavaScript. Если вывод заканчивается «ОК», тест пройден. - Нажмите «Питон».
Скопируйте и запустите сгенерированный код в интерпретаторе Python . Если вывод заканчивается «ОК», тест пройден. - Нажмите «PHP».
Скопируйте и запустите сгенерированный код в интерпретаторе PHP . Если вывод заканчивается «ОК», тест пройден. - Нажмите «Луа».
Скопируйте и запустите сгенерированный код в интерпретаторе Lua . Если вывод заканчивается «ОК», тест пройден. - Нажмите на «Дарт».
Скопируйте и запустите сгенерированный код в интерпретаторе Dart . Если вывод заканчивается «ОК», тест пройден.
Редактирование тестов генератора блоков
- Загрузите
tests/generators/index.html
в браузер. - Выберите соответствующую часть системы из раскрывающегося меню и нажмите «Загрузить». Блоки должны появиться в рабочей области.
- Вносите любые изменения или дополнения в блоки.
- Нажмите «XML».
- Скопируйте сгенерированный XML в соответствующий файл
tests/generators/
.