Правильная структура функциональных / приемочных испытаний

В настоящее время я работаю над созданием автоматизированного набора функциональных / приемочных тестов для проекта, но у меня нет большого опыта написания этих типов тестов, поэтому я хотел получить некоторый вклад в их правильное структурирование. В частности, я работаю с расширением Arquillian Graphene.

Например, у меня есть 3 теста: A, B и C.

TestA: проверяет вход в учетную запись приложения. Итак, если проверка прошла успешно, браузер должен находиться на домашней / информационной странице учетной записи.

TestB: тестирует изменение пароля учетной записи. Для этого потребуется войти в учетную запись, а затем протестировать функцию изменения пароля.

TestC: тестирует изменение электронной почты аккаунта. Для этого снова потребуется войти в учетную запись, а затем протестировать функцию изменения адреса электронной почты.

Если TestA выйдет из строя из-за проблемы с кодом входа, очевидно, что TestB и TestC также должны выйти из строя, поскольку они требуют входа в учетную запись.

Вопрос. Должны ли автоматические функциональные / приемочные тесты дублировать процесс, необходимый для завершения проверяемого теста? В этом случае TestB и TestC должны войти в учетную запись, прежде чем делать что-либо еще. Должен ли каждый тест явно вызывать что-то вроде:

/* ...initial test setup code here */
LoginPage.login(username, password);
assertTrue(onCorrectAccountPage);
AccountModification.changePassword(newPassword);

Или мне следует использовать какой-то способ имитации учетной записи в сеансе, который может использоваться тестами B и C, чтобы они не сработали, даже если TestA (фактический тест входа в систему) сработает?

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

Заранее спасибо. Надеюсь, мой вопрос не слишком запутан. :)


person whitlaaa    schedule 10.05.2012    source источник


Ответы (3)


Я лично сделал это так, чтобы каждый тестовый пример максимально воспроизводил действия пользователей, но вырезал места там, где это необходимо. Например, TestA: входит в систему, переходит на правильный веб-сайт, переходит в его административную систему, находит пользователя и удаляет часть информации о пользователе (например, имя), TestB: входит в систему, переходит на правильный веб-сайт, переходит на это административная система, находит пользователя и пытается полностью удалить его с помощью кнопки.

TestA и TestB попадают на одну и ту же страницу - страницу сведений о пользователе. Итак, в тесте A я могу делать все правильно, точно так же, как это делает пользователь, но в тесте B я перехожу непосредственно на страницу сведений для этого пользователя, а не выполняю правильную навигацию вручную. Почему?

Экономит время, зачем мне заново выполнять навигацию в тесте B, если тест A уже проверяет это в любом случае?

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

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

person Arran    schedule 10.05.2012
comment
Хорошо объяснено. Я думал в том же духе, но идея отсутствия дублирования настолько укоренилась в моем мыслительном процессе, что я не был уверен в этом обстоятельстве. Спасибо за Ваш ответ. - person whitlaaa; 11.05.2012

Это действительно вопрос для каждого проекта, так как оба подходы допустимы, но в разных ситуациях один должен быть более предпочтительным. В большой системе я предпочитаю запускать тестовый пример от начала до конца независимо от того, как часто повторяются шаги (например, я вхожу в систему для каждого теста). Я считаю, что это то, что уже сказал Арран (+1!). Причина, по которой я обычно это делаю, заключается в том, что иногда состояние, перенесенное с предыдущего экрана, может позже вызвать ошибку, и это то, что автоматические тесты отлично подходят для поиска. Однако с их помощью я убеждаюсь, что все тестовые данные действительны для подготовительных шагов, и стремлюсь к как можно более быстрому решению. Например, для входа в систему всегда должны быть правильные имя пользователя и пароль, тогда при успешной проверке входа в систему либо просто предположите, что он должен, и обработайте исключение позже, либо выполните поиск легко идентифицируемого элемента на странице вместо сложного «более важного» элемента.

С учетом сказанного, вы также можете писать тесты, которые проверяют несколько требований в виде своего рода функционального потока. Если поток прерывается, должны быть написаны тесты для определения области, в которой общая задача терпит неудачу. Я бы рекомендовал это только для небольших проектов или если тестирование не является приоритетом из-за нехватки ресурсов. Например, запуск теста, который входит в систему, выбирает элемент, кладет его в корзину, идет, чтобы оформить заказ, и платит, проверит всю эту функциональность и позволит команде исправить общий `` процесс '', а не просто несколько, потенциально отключенных , ошибки. Опять же, я считаю, что первый подход более тщательный, но он требует больше времени (но его стоит делать чаще :)).

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

person Nashibukasan    schedule 10.05.2012
comment
... тестирование нескольких требований в виде потока функциональности. Это то, что я планировал сделать в дополнение к более конкретным функциональным тестам, при условии, что у нас будет время и ресурсы. Ваш ответ определенно был не слишком длинным. :) Спасибо за вашу помощь. - person whitlaaa; 11.05.2012
comment
И что я сейчас делаю с этими «поточными» тестами, так это помещаю их в отдельную группу от моих основных тестов. Затем при запуске сборки их хорошо использовать как своего рода «дымовой тест» или тест базовой функциональности, потому что они занимают меньше времени, но все же тестируют основную функциональность приложения. Рад, что это помогло :) - person Nashibukasan; 12.05.2012

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

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

В JUnit вместо assertTrue с этой целью. Итак, ваш TestB станет:

/* ...initial test setup code here */
LoginPage.login(username, password);
assumeTrue(onCorrectAccountPage);
AccountModification.changePassword(newPassword);

Теперь, если acceptTrue терпит неудачу, тест просто игнорируется. Однако ваш TestA все равно не сработает, сообщая вам и вашему конечному пользователю, в чем реальная проблема.

person avandeursen    schedule 11.05.2012
comment
Это интересный подход, о котором я даже не думал. Я изучу это подробнее. - person whitlaaa; 11.05.2012