Тестируемая разработка веб-интерфейсов JavaScript - PullRequest
32 голосов
/ 01 апреля 2011

Это может показаться немного глупым, но я на самом деле немного запутался, как подходить к тестированию JavaScript для веб-интерфейсов.Насколько мне известно, типичная 3-уровневая архитектура выглядит следующим образом:

  1. Уровень базы данных
  2. Уровень приложения
  3. Уровень клиента

1 не имеет значения в этом вопросе.2 содержит всю программную логику («бизнес-логику») 3. Внешний интерфейс.

Я занимаюсь разработкой на основе тестирования для большинства проектов, но только для логики приложения, а не для внешнего интерфейса.Это потому, что тестирование пользовательского интерфейса является сложным и необычным в TDD и обычно не выполняется.Вместо этого вся логика приложения отделена от пользовательского интерфейса, поэтому эту логику просто протестировать.

Трехуровневая архитектура поддерживает это: я могу создать свой бэкэнд как REST API, который вызывается моим внешним интерфейсом.Как проходит тестирование JS?Для типичной трехуровневой архитектуры тестирование JS (т.е. JS на клиенте) не имеет особого смысла, не так ли?

Обновление: Я изменил формулировку вопроса с«Тестирование JavaScript в веб-интерфейсах» - «Разработка через веб-интерфейс JavaScript на основе тестирования», чтобы прояснить мой вопрос.

Ответы [ 6 ]

23 голосов
/ 20 апреля 2011

Помните, в чем смысл модульного тестирования: чтобы определенный модуль кода реагировал на некоторые стимулы ожидаемым образом.В JS значительная часть вашего кода (если у вас нет какой-либо инфраструктуры жизненного цикла, такой как Sencha или YUI) будет либо напрямую манипулировать DOM, либо делать удаленные вызовы.Чтобы проверить эти вещи, вы просто применяете традиционные методы юнит-тестирования внедрения зависимостей и насмешек / заглушек.Это означает, что вы должны написать каждую функцию или класс, который вы хотите выполнить модульным тестированием, чтобы принимать макеты зависимых структур.

jQuery поддерживает это, позволяя передавать документ XML во все функции обхода.В то время как вы обычно можете написать

$(function() { $('.bright').css('color','yellow'); }

, вместо этого вы захотите написать

function processBright(scope) {
    // jQuery will do the following line automatically, but for sake of clarity:
    scope = scope || window.document;

    $('.bright',scope).css('color','yellow');
}

$(processBright);

Обратите внимание, что мы не только извлекаем логику из анонимной функции и присваиваем ей имя, мы такжезаставить эту функцию принимать параметр области видимости.Когда это значение равно нулю, вызовы jQuery по-прежнему будут работать как обычно.Тем не менее, теперь у нас есть вектор для вставки фиктивного документа, который мы можем проверить после вызова функции.Модульный тест может выглядеть так:

function shouldSetColorYellowIfClassBright() {
    // arrange
    var testDoc = 
        $('<html><body><span id="a" class="bright">test</span></body></html>');

    // act
    processBright(testDoc);

    // assert
    if (testDoc.find('#a').css('color') != 'bright')
        throw TestFailed("Color property was not changed correctly.");
}

TestFailed может выглядеть так:

function TestFailed(message) {
    this.message = message;
    this.name = "TestFailed";
}

Ситуация схожа с удаленными вызовами, хотя вместо того, чтобы вводить какое-то средство, вы можете получитьпрочь с заглушкой маскировки.Скажем, у вас есть эта функция:

function makeRemoteCall(data, callback) {
    if (data.property == 'ok') 
        $.getJSON({url:'/someResource.json',callback:callback});
}

Вы бы проверили это так:

// test suite setup
var getJSON = $.getJSON;
var stubCalls = [];
$.getJSON = function(args) {
    stubCalls[stubCalls.length] = args.url;
}

// unit test 1
function shouldMakeRemoteCallWithOkProperty() {
    // arrange
    var arg = { property: 'ok' };

    // act
    makeRemoteCall(arg);

    // assert
    if (stubCalls.length != 1 || stubCalls[0] != '/someResource.json')
        throw TestFailed("someResource.json was not requested once and only once.");
}

// unit test 2
function shouldNotMakeRemoteCallWithoutOkProperty() {
    // arrange
    var arg = { property: 'foobar' };

    // act
    makeRemoteCall(arg);

    // assert
    if (stubCalls.length != 0)
        throw TestFailed(stubCalls[0] + " was called unexpectedly.");
}

// test suite teardown
$.getJSON = getJSON;

(Вы можете обернуть всю эту вещь в шаблон модуля , чтобы незамусорите глобальное пространство имен.)

Чтобы применить все это в тестовом режиме, вы просто должны сначала написать эти тесты.Это простой, без излишеств, и, что наиболее важно, эффективный способ модульного тестирования JS.

Каркасы, подобные qUnit, могут использоваться для запуска ваших модульных тестов, но это только небольшая часть проблемы.Ваш код должен быть написан для тестирования.Кроме того, фреймворки, такие как Selenium, HtmlUnit, jsTestDriver или Watir / N, предназначены для интеграционного тестирования, а не для модульного тестирования как такового.Наконец, ни в коем случае ваш код не должен быть объектно-ориентированным.Принципы юнит-тестирования легко спутать с практическим применением юнит-тестирования в объектно-ориентированных системах.Это отдельные, но совместимые идеи.

Стили тестирования

Следует отметить, что здесь демонстрируются два разных стиля тестирования.Первый предполагает полное незнание реализации processBright.Это может быть использование jQuery для добавления цветового стиля или выполнение собственных DOM-манипуляций.Я просто проверяю, что внешнее поведение функции соответствует ожидаемому .Во втором я предполагаю знание внутренней зависимости функции (а именно $ .getJSON), и эти тесты охватывают правильное взаимодействие с этой зависимостью .

Подход, который вы выбираете, зависит отваша философия тестирования и общие приоритеты и профиль затрат-выгод вашей ситуации.Первый тест относительно чистый.Второй тест прост, но относительно хрупок;если я изменю реализацию makeRemoteCall, тест будет прерван.Предположение о том, что makeRemoteCall использует $ .getJSON, по крайней мере, оправдано документацией makeRemoteCall.Есть пара более дисциплинированных подходов, но один рентабельный подход заключается в том, чтобы обернуть зависимости в функции-оболочки.Кодовая база будет зависеть только от этих оболочек, чьи реализации могут быть легко заменены тестовыми заглушками во время теста.

3 голосов
/ 13 апреля 2011

Существует книга Кристиана Йохансена под названием Разработка на основе тестов JavaScript , которая может вам помочь.Я только посмотрел на некоторые из примеров в книге (только что скачал образец в Kindle на днях), но это похоже на отличную книгу, посвященную этой самой проблеме.Вы можете проверить это.

(Примечание: я не имею никакого отношения к Кристиану Йохансену и не инвестирую в продажи книги. Просто выглядит как хорошая вещь, которая решает эту проблему.)

2 голосов
/ 13 апреля 2011

У меня есть похожее архитектурное приложение с уровнем клиента JS. В моем случае я использую собственную JS-платформу нашей компании для реализации клиентского уровня.

Эта структура JS создана в стиле ООП, поэтому я могу реализовать модульное тестирование для основных классов и компонентов. Кроме того, чтобы охватить все взаимодействия с пользователем (которые не могут быть рассмотрены с помощью модульного тестирования), я использую Selenium WebDriver , чтобы выполнить интеграционное тестирование визуальных компонентов инфраструктуры и протестировать их в разных браузерах.

Таким образом, TDD можно применять для разработки JavaScript, если тестируемый код написан в ООП-стиле. Также возможен интеграционный тест (и его можно использовать для какого-то TDD).

1 голос
/ 14 апреля 2011

Также обратите внимание на QUnit для модульных тестов методов и функций JavaScript.

0 голосов
/ 19 апреля 2011

В нашей компании мы используем jsTestDriver . Это многофункциональная среда для тестирования веб-интерфейса.

Взгляните на это.

0 голосов
/ 01 апреля 2011

Вы можете протестировать свое приложение с точки зрения пользователя с помощью таких инструментов, как Rational Functional Tester , HP tools или другого эквивалентного программного обеспечения.

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

К сожалению, эти инструменты очень дороги, поэтому могут быть другие эквиваленты (и ябыло бы интересно узнать о таких инструментах).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...