Какой вывод сделал для себя. Внутри теста может быть сколько угодно проверок и логично их ставить после каждого шага теста. Например открылась страница, проверь что открылась именно та, что нужно. Перед тем как нажимать на кнопку, проверь, что такая кнопка есть, чтобы не получить Exception.
В целом интересная тема, но главное чтобы сам тест делился на 3 части:
- setUp
- test
- tearDown
А где ставить проверки это должен подсказывать здравый смысл ну или тестировщик, который написал тестовый сценарий.
Юнит тесты отличаются от функциональных, потому при автоматизации функциональных тестов можно и нужно переиспользовать некоторые паттерны, но не все подряд. Тут все зависит от того, как построен ручной тест, ведь автоматизация не происходит сама по себе. Есть ручной тест, он выполняется, потом нужно его выполнять быстро, потому создается автоматический тест. Если в ручном тесте 100 шагов и 20 проверок, то автоматичский тест будет зеркальным отображением ручного теста.
Например,
{syntaxhighlighter brush: java;fontsize: 100; first-line: 1; } @Test
public void viewAffiliate_existedAffiliate_detailsAccessible(){
//combined with success affiliate creation test case
AffiliatesListPage affiliate = createAffiliateWithRandomData()
assertThat("affiliate is created", affiliate.returnCreationMessage(), endsWith("created"))
ViewAffiliatePage view = affiliate.openAndViewAffiliateById(affiliate.returnIdOfCreatedAffiliate())
assertThat("affiliate name is in and displayed", view.getPropertyValueByName("Name"), containsString("name"))
assertThat("addiliate description is in and displayed", view.getPropertyValueByName("Description"), containsString("description"))
}{/syntaxhighlighter}
Опишу свой взгляд на данную проблему как разработчика:
1. Модульные тесты действительно сильно отличаются от функциональных. Один assert в них рекомендуется для лучшей понимаемости и быстрого анализа результатов выполнения. А выполняются они ооочень часто. Если у вас физически несколько assert в одном тесте, то рекомендуется объединить их в один логический assert (это метод в большей части случаев, иногда более сложная конструкция). Этот подход заставляет вас писать тесты, которые тестируют ровно один аспект функциональности.
2. В функциональных тестах правила не так жестки, но я предпочитаю их придерживаться и в этом случае. Это упрощает понимание тестов и анализ их результатов.
3. Многие ошибочно (на мой взгляд) применяют assert для проверки промежуточных состояний (как в выше приведенных примерах). Это добавляет путаницы, потому что вы тестировали не эту проверку, а ту, которая расположена в конце теста. Вместо assert мне больше нравится применять проверки с пробрасыванием exception (благо в Java есть классы Validate и другие утилиты). Иногда проверку можно не делать вовсе, потому что следующая операция и так провалится. А exception укажет вам на номер строки, где это произошло.
4. Ну и в разрезе BDD - assert допустим только в секции //Then (из //Given, //When, //Then). :)
да конечно, этот пример можно упростить, пре и пост кондишины как структура присутствует и можно разбить еще на дополнительные функции, но мне вряд ли вериться, что функциональные тесты будут всего лишь с одной проверкой, так как еще повторюсь, автоматические тесты являются отображением ручных вне зависимости. Я тоже стараюсь придерживаться принципа простоты, но в реальности не все так получается. У тебя в много таких функциональных тестов с одной проверкой?
Где-то я согласен с Колей, по поводу assert. Но на некоторых проектах было требование, чтобы ошибки автотестов были человека читаемые.
Да, BDD частично решает эту проблему. Но все равно по стек трейсу людям было тяжело отследить ошибку. Применяя DSL конечно проще понять, что делал тест на своем пути, но это пугает тестировщиков. Потому промежуточная проверка, хоть и усложняет логическую цепочту теста, но на выходе дает человеко-понятное сообщение, почему этот тест свалился.
Использую ваш же пример - насколько я понимаю, то в данном случае сообщение в случае ошибки трудно назвать человекочитаемым
public void setUp() { projectPage = loginPage.openAndLogin(); assertThat(projectPage.isOpened(), is(true)); }
Как вариант можно сделать несколько специфических для тестируемого приложения assert-методов для проверки основных состояний (открыта нужная страница, есть соответствующий текст на странице) и использовать их для повышения читаемости.
Поддержу мнение Николая, что ассертить "незначимые" шаги теста (по сути шаги, которые являются подготовительными действиями для проверок, которые и являются целью теста) не обязательно - можно конечно, если есть желание и достаточно времени при написании тестов. Стектрейс не должен пугать тестировщика - ему приходится и не с такими вещами сталкиваться при тестировании, тем более, что в большинстве случаев стектрейс читается довольно просто.
Здесь есть другой вопрос - как вы поступаете в случае, когда тесты завершаются с ошибками? Только ли смотрите сообщение, выдаваемое тестом, и на основании этого документируете ошибку? Или все-таки выполняете руками все те действия, которые выполняются тестом? Из личного опыта могу сказать, что всегда повторяю вручную все те действия, которые приводят к ошибках в автотестах. И здесь на первое мето выходит не текст сообщения об ошибке, а читаемость тестов - но это уже другой вопрос.
Вообще для юнит-тестов правило "один тест - один assert" выглядит разумно, для случая функциональных тестов должно быть столько проверок, сколько требует тест-кейс и здравый смысл (не всегда эти два понятия совпадают :))
Неудачный пример. В идеале страница должна бросать исключение с текстом ошибки если у нее не получилось открыться. Тогда будет единообразие - ведь если элемент не найден, то будет исключение, а не assert error. :) И дубликатов не будет по всем тестам. :)
Пример не плох, просто другой подход Этот пример не плохой, просто, тут подход другой, я бы сказал альтернативный. Я сторонник того, чтобы страница сама себя вызывала. Но, в таком случае нужно принимать то, что очень много действий делаются неявно, по дефолту. Это уменьшает код самого теста. С другой стороны, код Андрея заточен под конкретный тест-сьют, не дуплицируется, потому что вынесен в отдельный метод setUp. И его легко модифицировать для конкретного сьюта, без влияния на тесты из других сьютов.
Хотя, я люблю писать по меньше кода, и поэтому у меня в каждой страницы есть специальный метод Invoke(), который обязан запустить страницу.
Вот пример обычного теста:
{syntaxhighlighter brush: csharp;fontsize: 100; first-line: 1; }[TestMethod]
public void AdminPageHasLinkToDefaultMessageScreen()
{
var adminPage = new AdministratorMenuPage();
adminPage.Invoke();
[When(@"I am on the \{Administrator} page")]
[Given(@"I am on the \{Administrator} page")]
[Then(@"after that I go back to the \{Administrator} page")]
public void WhenIAmOnTheAdministratorPage()
{
adminPage.Invoke();
}
{/syntaxhighlighter}
Плюс такого подхода в том, что я не использую лишние setup-методы и объем кода становится меньше. Минусы, это то, что внутри используется очень много магии
Мсье, я так понимаю, что у вас в тест-классе не один единственный тест, и метод setUp автоматически дергается фреймворком (TestNG/JUnit) в начале каждого теста? Если так, то в моем случае, метод WhenIAmOnTheAdministratorPage() вызывается в некоторых сценариях по нескольку раз. В первом сценарии у меня это When – ну да, тот же setUp потому что выполняется вначале тесткейса. Во втором случае, это Given – тот же сетап, но и одновременно And (в конце второго сценария) – это уже будет tearDown.
В третьем сценарии, который я не хочу приводить, ибо он очень специфичен к продукту, Given у меня отличается, и Админка запускается лишь в конце тесткейса для проверки на то, как мои действия вначале теста повлияли на админку.
{syntaxhighlighter brush: ruby;fontsize: 100; first-line: 1; }Scenario: The Administrator page should have valid set of links and captions
When I am on the {Administrator} page
Then I can see the following set of references:
| Web link description | Expected text |
| CENSORED | CENSORED |
| CENSORED | CENSORED |
| CENSORED | CENSORED |
| CENSORED | CENSORED |
And I can see the {Administrator} page title is "Administrator Menu"
Scenario Outline: Open each page from the Admin Menu
Given I am on the {Administrator} page
When I click the {<Web link>}
Then I can see the page {<Web page class>} with title {<Web page title>}
And after that I go back to the {Administrator} page
Examples:
| Web link | Web page class | Web page title |
| CENSORED | CENSORED | CENSORED |
| CENSORED | CENSORED | CENSORED |
| CENSORED | CENSORED | CENSORED |
| CENSORED | CENSORED | CENSORED |
{/syntaxhighlighter}
И да, Мсье, хочу подтвердить Вашу возможную мысль, Cucumber/Specflow/Duke4Nuke и прочие это еще то извращение
И оффтопик (холивар): Основная фишка Specflow в том, что этот фреймворк умеет компилировать BDD Features/Scenarios в тесты для MS Test, NUnit, xUnit. Я могу запускать BDD-тесты и обычные юнит-тесты из одного проекта. Есть ли нечто подобное в мире JUnit/TestNG?
Все верно, только в testNG есть еще такие аннотации как @BeforeMethod @BeforeClass @BeforeTest @BeforeSuite и можно самому настроить как запускать свои методы SetUp. Что ничем не отличается от Given
Как по мне лучше, тогда писать универсальные и гибкие ассерты, а то появляется еще один дополнительный уровень логических проверок, в которых будет комбинации ассертов. Т.е. например, мне надо будет логиниться под пользователем у доп. права, и для него мне надо будет делать еще один похожий метод только с еще одной проверкой.
В Cucumber можно переиспользовать сами Given\When\Then, то есть например внутри какого-то, то есть что-то типа такого:
{syntaxhighlighter brush: ruby;fontsize: 100; first-line: 1; }When "I open reports tab" do
Given "I'm on landing page"
And 'Click on the "Reports" link'
end{/syntaxhighlighter}
В SpecFlow я такого не видел. Там я еще наталкивался на ряд ограничений, которые меня несколько расстраивали, сейчас не вспомню. Но SpecFlow - это далеко не единственная подобная штуковина для C#. Есть еще NBehave, у которого для студии есть прикольный плагин, который позволяет делать автодополнение. То есть вы пишете какую-то фразу в feature-файле, а он вам варианты подбрасывает.