Использование JavaScript не ограничивается клиентским кодом в браузере или серверным кодом на основе NodeJS . Многие проекты на основе JVM используют его как внутренний язык сценариев. Тестирование такого рода функциональности не является ни простым, ни стандартным. В этом посте я намерен продемонстрировать подход для тестирования JavaScript в серверной среде JVM с использованием зрелых инструментов, таких как Jasmine , Spock и Nashorn .
Использование JavaScript в качестве движка сценариев внутри приложения JVM существенно отличается от кодирования на стороне клиента. И, к сожалению, в настоящее время нет промышленных стандартных инструментов для его тестирования.
Что касается существующих подходов, найденных в Интернете, я бы хотел выделить следующие недостатки:
- отсутствие интеграции со средствами сборки и непрерывной интеграции (Maven, Gradle, Jenkins и т. д.)
- недостаточное сотрудничество с IDE
- нет возможности запустить один комплект или тест из IDE
- невозможно просмотреть отчеты о выполнении теста из IDE
- тесная связь с браузерной средой
- нет возможности использования пользовательских исполнителей
JavaScript
Насколько я видел, большинство проектов тестируют свои встроенные бизнес-скрипты, вызывая обработчик движка JS, передавая ему тестируемый скрипт и проверяя его, проверяя побочные эффекты на движок или макеты после выполнения скрипта.
Подобные подходы обычно имеют схожие недостатки:
- трудно что-то заглушить или посмеяться в коде JS, обычно заканчивая взломом
prototypeJS - нужно слишком много оркестровки для насмешливой среды для сценария
- трудно организовать тесты в наборы и сообщать об ошибках выполнения тестов
- предыдущие причины создание пользовательских рамок тестового набора для конкретного проекта
- не используя существующие инструменты и инфраструктуры тестирования JavaScript
Поэтому, исходя из необходимости удобного встроенного тестирования JavaScript в проектах JVM, я создал этот пример установки. Для достижения наших целей будут использованы следующие инструменты.
-
Jasmineявляется одним из самых известных инструментов TDD / BDD для JavaScript -
Spock— отличный фреймворк для тестирования JVM на базе Junit и Groovy -
Nashorn— это современный скриптовый движок, представленный в JDK8.
Индивидуальный JavaScript-бегун (на основе Nashorn)
Нет необходимости согласовывать стандарты в не браузерной среде JS, поэтому обычно разработчики расширяют механизм сценариев с помощью пользовательских функций, встроенных переменных и т. Д. Чрезвычайно важно использовать один и тот же бегун как для рабочих целей, так и для целей тестирования.
Давайте рассмотрим, что у нас есть такой настраиваемый бегун, который принимает имя сценария и карту предопределенных переменных в качестве параметров и возвращает полученное значение выполненного сценария.
JavaScriptRunner.java
|
1
2
3
4
5
6
7
8
|
public class JavaScriptRunner { public static Object run(String script, Map<String, Object> params) throws Exception { ScriptEngineManager factory = new ScriptEngineManager(); ScriptEngine engine = factory.getEngineByName("nashorn"); engine.getBindings(ScriptContext.ENGINE_SCOPE).putAll(params); return engine.eval(new InputStreamReader(JavaScriptRunner.class.getResourceAsStream(script))); (1) }} |
| 1 | источник сценария ищется в classpath. |
Настройка Жасмин
Чтобы начать использовать фреймворк Jasmine нам нужно:
- скачайте Jasmine и распакуйте его в папку
/jasmine/jasmine-2.1.2каталоге ресурсов проекта - пользовательский скрипт начальной загрузки, поскольку
Jasmineне поддерживает платформы на основе JVM
jasmine2-bootstrap.js
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
var loadFromClassPath = function(path) { (1) load(Java.type("ua.eshepelyuk.blog.nashorn.Jasmine2Specification").class.getResource(path).toExternalForm());};var window = this;loadFromClassPath("/jasmine/jasmine-2.1.2/jasmine.js");loadFromClassPath("/jasmine/jasmine2-html-stub.js"); (2)loadFromClassPath("/jasmine/jasmine-2.1.2/boot.js");load({script: __jasmineSpec__, name: __jasmineSpecName__}); (3)onload(); (4)jsApiReporter.specs(); (5) |
| 1 | вспомогательная функция, разрешающая путь к сценарию из местоположения пути к классу. |
| 2 | Специфичный для Nashorn код, корректирующий Jasmine для не браузерных сред. Не является частью распространения Jasmine . |
| 3 | загрузка исходного кода набора тестов, подробности см. в следующем разделе. |
| 4 | фальшивое событие load браузера, которое должно запустить выполнение набора тестов. |
| 5 | это значение будет возвращено как результат скрипта. |
Преобразуйте отчет Жасмин в тесты Спока
Имея JS executor и скрипт начальной загрузки для Jasmine мы могли бы создать тест JUnit чтобы перебрать результаты набора и проверить, все ли успешно. Но это станет кошмаром, чтобы понять, какой именно тест провалился и в чем причина неудачи. Нам действительно хотелось бы иметь возможность представлять каждую спецификацию Jasmine как тест JUnit , чтобы любой инструмент Java мог подобрать и проверить результаты. Вот почему Spock может быть ответом на проблему с помощью Data Driven Testing, который позволяет разработчику объявлять список входных данных и для каждого элемента этого набора данных будет создаваться и выполняться новый тест. Это очень похоже на Parametrized Test of Junit но гораздо более мощную реализацию.
Поэтому идея будет заключаться в том, чтобы рассматривать результаты набора тестов Jasmine полученные после запуска загрузочного скрипта, как массив входных данных, каждый элемент которых будет передан в тест Spock . Затем сам тест предоставит утверждение для правильного отчета об успешных и неудачных тестах, т.е. утверждение должно проверять состояние спецификации Jasmine .
- если статус находится в состоянии
pendingилиpassed, это означает, что спецификация либо игнорируется, либо успешна - в противном случае тест
Spockдолжен выдать ошибку подтверждения, заполнив исключение подтверждения, заполненное сообщениями о сбоях, сообщеннымиJasmine
Jasmine2Specification.groovy
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
abstract class Jasmine2Specification extends Specification { @Shared def jasmineResults def setupSpec() { def scriptParams = [ "__jasmineSpec__" : getMetaClass().getMetaProperty("SPEC").getProperty(null), (1) "__jasmineSpecName__": "${this.class.simpleName}.groovy" ] jasmineResults = JavaScriptRunner.run("/jasmine/jasmine2-bootstrap.js", scriptParams) (2) } def isPassed(def specRes) {specRes.status == "passed" || specRes.status == "pending"} def specErrorMsg(def specResult) { specResult.failedExpectations .collect {it.value}.collect {it.stack}.join("\n\n\n") } @Unroll def '#specName'() { expect: assert isPassed(item), specErrorMsg(item) (3) where: item << jasmineResults.collect { it.value } specName = (item.status != "pending" ? item.fullName : "IGNORED: $item.fullName") (4) }} |
| 1 | предоставление исходного кода набора Jasmine как переменной jasmineSpec , доступной для исполнителя JS. |
| 2 | фактическое исполнение Jasmine свиты. |
| 3 | для каждого результата набора мы assert он либо был успешным, выдавая ошибку подтверждения с сообщением, созданным Жасмином, при ошибке. |
| 4 | дополнительная переменная поставщика данных для выделения игнорируемых тестов. |
Полный пример
Давайте создадим набор тестов для простой функции JavaScript.
mathUtils.js
|
1
2
3
|
var add = function add(a, b) { return a + b;}; |
Используя базовый класс из предыдущего шага, мы могли бы создать пакет Spock содержащий тесты JavaScript. Чтобы продемонстрировать все возможности нашего решения, мы создадим успешный, неудачный и проигнорированный тест.
MathUtilsTest.groovy
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
class MathUtilsTest extends Jasmine2Specification { static def SPEC = """ (1)loadFromClassPath("/js/mathUtils.js"); (2)describe("suite 1", function() { it("should pass", function() { expect(add(1, 2)).toBe(3); }); it("should fail", function() { expect(add(1, 2)).toBe(3); expect(add(1, 2)).toBe(0); }); xit("should be ignored", function() { expect(add(1, 2)).toBe(3); });})"""} |
| 1 | Фактический код набора Jasmine представлен в виде String переменной. |
| 2 | загрузка тестируемого модуля с использованием функции, унаследованной от jasmine-bootstrap.js . |
Инъекция языка IntelliJ Idea
Несмотря на то, что этот микро-фреймворк должен работать во всех IDE, его наиболее удобно использовать в IntelliJ IDEA благодаря внедрению языка . Функция позволяет встраивать произвольный язык в файл, созданный на другом языке программирования. Таким образом, мы можем встроить блок кода JavaScript в спецификацию Spock написанную на Groovy.
Плюсы и минусы решения
преимущества
- использование стандартных инструментов тестирования для обоих языков
- бесшовная интеграция с инструментами сборки и инструментами непрерывной интеграции
- возможность запуска одного пакета из IDE
- запускать одиночный тест из конкретного пакета благодаря целенаправленной функции Jasmine
Недостатки
- нет чистого способа обнаружения конкретной строки исходного кода в случае исключения теста
- немного
IntelliJ IDEAориентированная установка
PS
Для этого примера проекта я использовал современный движок Nashorn от JDK8. Но на самом деле нет никаких ограничений на это. Тот же подход был успешно применен для проектов, использующих старый движок Rhino . И опять же, Jasmine — это мое личное предпочтение. Благодаря дополнительному QUnit коду можно настроить использование Mocha , QUnit и так далее.
- Полный код проекта доступен на My GitHub .
| Ссылка: | Тестирование JavaScript JVM на стороне сервера с Jasmine, Spock и Nashorn от нашего партнера по JCG Евгения Шепелюка в блоге jk . |

