Использование JavaScript
не ограничивается клиентским кодом в браузере или серверным кодом на основе NodeJS
. Многие проекты на основе JVM
используют его как внутренний язык сценариев. Тестирование такого рода функциональности не является ни простым, ни стандартным. В этом посте я намерен продемонстрировать подход для тестирования JavaScript
в серверной среде JVM с использованием зрелых инструментов, таких как Jasmine
, Spock
и Nashorn
.
Использование JavaScript в качестве движка сценариев внутри приложения JVM существенно отличается от кодирования на стороне клиента. И, к сожалению, в настоящее время нет промышленных стандартных инструментов для его тестирования.
Что касается существующих подходов, найденных в Интернете, я бы хотел выделить следующие недостатки:
- отсутствие интеграции со средствами сборки и непрерывной интеграции (Maven, Gradle, Jenkins и т. д.)
- недостаточное сотрудничество с IDE
- нет возможности запустить один комплект или тест из IDE
- невозможно просмотреть отчеты о выполнении теста из IDE
- тесная связь с браузерной средой
- нет возможности использования пользовательских исполнителей
JavaScript
Насколько я видел, большинство проектов тестируют свои встроенные бизнес-скрипты, вызывая обработчик движка JS, передавая ему тестируемый скрипт и проверяя его, проверяя побочные эффекты на движок или макеты после выполнения скрипта.
Подобные подходы обычно имеют схожие недостатки:
- трудно что-то заглушить или посмеяться в коде JS, обычно заканчивая взломом
prototype
JS - нужно слишком много оркестровки для насмешливой среды для сценария
- трудно организовать тесты в наборы и сообщать об ошибках выполнения тестов
- предыдущие причины создание пользовательских рамок тестового набора для конкретного проекта
- не используя существующие инструменты и инфраструктуры тестирования 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 . |