Я пытаюсь создать масштабируемую структуру функций и определений шагов для большого приложения, и в первую очередь я пытался связать файлы step_definition с функциями, чтобы я мог использовать один и тот же шаблон шагов для разных определений шагов.
Я показываю свой текущий пример:
Моя структура папок:
/features/sample.feature
/features/example.feature
/features/step_definitions/sample_steps.js
/features/step_definitions/example_steps.js
/features/step_definitions/common/common_steps.js
В моем sample.feature
у меня есть:
Scenario: Launching Cucumber
Given I have some step definitions
When I check some step definition with parameter "any"
Then I should see all green "sample"
В моем example.feature
у меня есть:
Scenario: Launching Cucumber
Given I have some step definitions
When I check some step definition with parameter "any"
Then I should see all green "example"
Шаги Given
и When
определены в файле /common/common_steps.js
и работают нормально.
Шаг Then
определяется как для sample_steps.js
, так и для example_steps.js
, но по-разному.
В моем sample_steps.js у меня есть:
Then('I should see all green {stringInDoubleQuotes}', (arg) => {
if (arg !== 'sample') {
throw 'I should see all green when the argument is "sample"';
}
return;
});
И, наконец, в моем example_steps.js у меня есть:
Then('I should see all green {stringInDoubleQuotes}', (arg) => {
if (arg !== 'example') {
throw 'I should see all green when the argument is "example"';
}
return;
});
Моя главная цель - сделать здесь все зеленым, но, конечно, это не работает, и я получаю эту очевидную ошибку:
Multiple step definitions match:
I should see all green {stringInDoubleQuotes} - features\step_definitions\example_steps.js:6
I should see all green {stringInDoubleQuotes} - features\step_definitions\sample_steps.js:6
Я знаю, что в огурце-jvm мы можем указать атрибут glue
, который связывает функции и step_definitions, и это именно то, что я ищу, но в огурце-js. Пример на Java:
@RunWith(Cucumber.class)
@Cucumber.Options( glue = { "com.app.stepdefinitions.common", "com.app.stepdefinitions.sample" } )
public class SampleFeature{
}
@RunWith(Cucumber.class)
@Cucumber.Options( glue = { "com.app.stepdefinitions.common", "com.app.stepdefinitions.example" } )
public class ExampleFeature{
}
Как я могу добиться того же, что и cucumbr-jvm, используя огурец-js?
Я пытаюсь создать масштабируемую структуру функций и определений шагов для большого приложения, и в первую очередь я пытался связать файлы step_definition с функциями, чтобы я мог использовать один и тот же шаблон шагов для разных определений шагов.
Очень интересно. Cucumber-js не имеет ничего встроенного, как пример Cucumber-Java, который вы привели. Идея, которая мне нравится в таких вещах, заключается в том, чтобы внедрить это логическое переключение в настройку вашего мира или инстанс. В любом случае вы получите только одно определение шага. У вас может быть несколько определений мира, между которыми вы переключаетесь при определении кода поддержки, или один экземпляр мира, который предоставляет различные методы в зависимости от контекста. В настоящее время мы не предоставляем файл текущего сценария для выполнения шагов, но вы можете использовать теги для определения своего контекста.
У нас на самом деле есть такая же потребность в этом. Мы используем nightwatch-cucumber для запуска тестов селена, и наше единственное решение на данный момент — добавить префикс к каждому шагу:
Given [comp1] I click on "Open dialog"
против
Given [comp2] I click on "Open dialog"
Это помогает нам избежать двусмысленных определений шагов, но приводит к действительно нечитаемым файлам функций.
Я пытался возиться с исходным кодом огурца.js, но не нашел хороших советов по добавлению поддержки этой функции.
Можно ли это реализовать с помощью каких-то хуков или альтернативных миров?
@leipert, какой пользовательский интерфейс вы представляете? Я считаю, что эта логика должна жить в мире или поддерживать несколько объектов мира. Затем определения шага просто имеют дело с разбором совпадений корнишонов и передачей их в соответствующую мировую функцию. Мы могли бы добавить опцию CLI для выбора того, какой объект мира использовать.
На данный момент, если вы хотите иметь несколько определений миров/шагов, вы можете добиться этого, поместив свой код в отдельные папки и требуя только одного из них для каждого запуска (используя параметр --require CLI).
Ну а "огуречная книга" специально отговаривает такой способ оформления ступенек. Шаг разделен между сценариями по дизайну, одно и то же предложение должно иметь одинаковое значение, несмотря на то, что функция его использует. Как только вы представите объем шагов, очень легко создать языковую ловушку.
Пока только теги близки к вашей цели в cucuber.js. Но только хуки могут объявлять, что они специфичны для тегов. Если вы уверены, что это правильный путь для ваших людей, вы можете изобрести DSL, возможно, просто как [название функции] в пошаговом шаблоне.
Спасибо, @pinxue . Очень полезный ответ. Однако я не могу этого понять:
Ну а "огуречная книга" специально отговаривает такой способ оформления ступенек.
Одна и та же фраза действия может иметь разное значение в разных контекстах. Но это нормально. Хорошо знать варианты, которые у меня есть. На самом деле, мы уже идем по внутреннему DSL, чтобы достичь этого,
Большое спасибо.
Спасибо за ваши ответы @pinxue и @robsonrosa.
Вот мои аргументы в пользу определений шага с ограниченной областью действия:
Я вижу следующие подходы к решению проблемы для нас:
glue
для огурца.jscucumber.js
При этом мы, вероятно, выберем 3., если сопровождающие огурца.js считают, что определения шагов с ограниченной областью действия выходят за рамки (каламбур) для этого проекта.
@robsonrosa Мне было бы интересно ваше решение, если оно у вас есть.
Объем ИМО определенно необходим. По мере роста вашего приложения количество функций увеличивается, и вы получите противоречивые описания в разных контекстах.
В моей компании наш продукт имеет сотни функций, а QA имеет тестовые примеры в диапазоне 100 000.
Если мы будем использовать cucumber
, мы обязательно столкнемся с этой проблемой.
Да, я думаю, вам следует учитывать контекст, а не область действия.
У вас может быть большинство, если не все, определений ваших шагов в контексте по умолчанию (или без контекста), но должен быть способ указать контекст без необходимости использования пользовательского DSL.
Эти тесты должны быть написаны BA и QA, и любой нестандартный DSL неизбежно создаст путаницу и сопротивление.
Функция/сценарий уже предоставляет контексты по определению, поэтому синтаксис Gherkin имеет отступ.
Добавление тегов и пользовательских DSL — это обходной путь ограничения реализации (т. е. хак, IMO), а не решение.
Может быть, вы можете рассмотреть это, рассматривая https://github.com/cucumber/cucumber-js/issues/745 ?
Как насчет расширения Scenario
из defineStep
и передачи {Given, When, Then}
в обратный вызов?
то есть:
import {defineSupportCode} from 'cucumber'
defineSupportCode(({ Scenario, Given, When, Then }) => {
Given(...)
When(...)
Then(...)
Scenario('<match scenario description>', ({ Given, When, Then}) => {
Given('<take precedent of non-contexted>', ...)
...
})
})
Я изучаю BDD на http://fitnesse.org/ , поэтому я могу смотреть на BDD иначе, чем в сообществе огурцов.
Пейджинг @richardlawrence
Это область, где Cucumber особенно самоуверен. Он основан на убеждении, что команды должны разработать вездесущий язык, в котором слова и фразы означают ровно одно и то же в контексте приложения и используются последовательно. Сохранение глобальных определений шага поддерживает положительное давление, чтобы избежать двусмысленности.
В редких случаях, когда приложение имеет несколько отдельных ограниченных контекстов (в терминах DDD), вы просто разделите свой набор Cucumber по тем же линиям, что и ваше приложение, чтобы отразить эту границу, и определения шагов будут глобальными в каждом ограниченном контексте.
Статья о работе с Cucumber и создании границ. Он реализует некоторые идеи на этой странице, но не предлагает никаких замечательных решений, поскольку @richardlawrence упомянул, что вы не можете настроить Cucumber, чтобы сосредоточиться на одном конкретном классе для определения шагов. http://confessionsofanagilecoach.blogspot.com/2017/05/teaching-cucumbers-about-boundaries.html
Как сказал @leipert , глобальные переменные - это плохо. Я думаю, что те из нас, кто работает в мире CucumberJVM, понимают только половину истории. Cucumber (не JVM) использует концепцию tidy World, которая представляет собой глобальное пространство памяти на время действия Scenario . После выполнения сценария он уничтожается. Это похоже на хорошее решение. Но... Я не вижу хорошей реализации этого шаблона в CucumberJVM. Так что, если Cucumber заставляет нас иметь плоское/глобальное пространство имен для определений шагов, а CucumberJVM имеет четкий шаблон проектирования для реализации шаблона World, то я немного счастливее. До этого момента шаблон CucumberJVM + World == слишком сложные решения. До сих пор все, что я видел, было сложнее, чем просто позволить мне контролировать, какие пошаговые функции идут с каким файлом функций. До сих пор альтернативы, которые я видел, не давали мне ничего более ценного для всех усилий/сложностей. Другие типы огурцов имеют лучшие мировые решения.
В конечном счете, даже с шаблоном World я все еще недоволен, потому что знаю, что при переходе от файла функций к шагам будет потеря информации. Если я приложу большие усилия: приведу свои файлы функций в хорошую организацию, создам хорошие имена файлов функций, объявлю свою функцию разумным способом, назову свои сценарии разумным способом - весь контекст будет выброшен в окно, и я буду вынужден для работы с глобальным пространством имен определения шага.
Я могу думать только о том, чтобы сохранить отношения вне проверок системы и просто добавить подстановочные знаки на пути к тестам и заставить их работать, даже если для этого потребуется изменить какую-либо среду с открытым исходным кодом. может дать ему шанс по мере роста нашего проекта.
Я понимаю вашу цель, но я бы не рекомендовал ее.
Я бы также рекомендовал иметь явные файлы .feature либо с фоновым оператором, либо с дополнительным шагом Given , который делает это явным.
В качестве альтернативы вы можете создать два разных файла конфигурации.
Один для пробы , другой для примера.
Затем вы можете указать на разные папки шагов.
@robsonrosa @leipert Я разделяю ваше мнение
Спустя почти два года после первого поста...
Достигли ли вы какого-либо прогресса в этом? Любое обходное решение?
@cristianmercado19 Извините, нет. Я больше не использую огуречный js.
Упс... Мне нравится способ написания функционального файла, полностью изолированного от реализации шага.
Я пытался достичь той же цели с _mocha_, но я не доволен.
Если у вас есть другая альтернатива, которая соответствует моей цели, я буду рад попробовать.
Благодарим вас за помощь @leipert .
Эта ветка была автоматически заблокирована, так как после ее закрытия не было никаких действий в последнее время. Пожалуйста, откройте новую проблему для связанных ошибок.
Самый полезный комментарий
Объем ИМО определенно необходим. По мере роста вашего приложения количество функций увеличивается, и вы получите противоречивые описания в разных контекстах.
В моей компании наш продукт имеет сотни функций, а QA имеет тестовые примеры в диапазоне 100 000.
Если мы будем использовать
cucumber
, мы обязательно столкнемся с этой проблемой.Да, я думаю, вам следует учитывать контекст, а не область действия.
У вас может быть большинство, если не все, определений ваших шагов в контексте по умолчанию (или без контекста), но должен быть способ указать контекст без необходимости использования пользовательского DSL.
Эти тесты должны быть написаны BA и QA, и любой нестандартный DSL неизбежно создаст путаницу и сопротивление.
Функция/сценарий уже предоставляет контексты по определению, поэтому синтаксис Gherkin имеет отступ.
Добавление тегов и пользовательских DSL — это обходной путь ограничения реализации (т. е. хак, IMO), а не решение.
Может быть, вы можете рассмотреть это, рассматривая https://github.com/cucumber/cucumber-js/issues/745 ?
Как насчет расширения
Scenario
изdefineStep
и передачи{Given, When, Then}
в обратный вызов?то есть: