Приемочное тестирование используется для определения соответствия требованиям спецификации. Он должен работать в среде, максимально похожей на рабочую. Поэтому, если ваше приложение развернуто в Openshift, вам потребуется параллельная учетная запись с той, которая используется в рабочей среде для запуска тестов. В этом посте мы собираемся написать приемочный тест для приложения, развернутого в Openshift, которое использует MongoDb в качестве базы данных.
Развернутое приложение представляет собой простую библиотеку, которая возвращает все книги, доступные для кредитования. Это приложение использует
MongoDb для хранения всей информации, связанной с книгами.
Итак, давайте начнем с описания цели, функции, пользовательской истории и критериев приемлемости для предыдущих приложений.
Учитывая, что я хочу взять книгу
Когда я нахожусь на странице каталога
Тогда я должен увидеть информацию о доступных книгах: Властелин банок — 1299 — LOTRCoverUrl, Хоббит — 293 — HobbitCoverUrl
Обратите внимание, что это очень простое приложение, поэтому критерии принятия тоже просты.
Для этого примера нам понадобятся две тестовые среды: первая для написания и запуска приемочных тестов, а другая для управления
бэкэндом NoSQL . В этом посте мы собираемся использовать
Thucydides для
ATDD и
NoSQLUnit для работы с
MongoDb .
Приложение уже развернуто в
Openshift , и вы можете взглянуть на
https://books-lordofthejars.rhcloud.com/GetAllBooks
Thucydides — это инструмент, разработанный для облегчения написания автоматизированных приемочных и регрессионных тестов.
API WebDriver для доступа к
элементам HTML- страницы. Но это также помогает вам организовать ваши тесты и пользовательские истории с помощью конкретной модели программирования, создавать отчеты о выполненных тестах и, наконец, также измерять функциональное покрытие.
Чтобы написать приемочные тесты с
Thucydides следующие шаги должны быть выполнены.
- Прежде всего, выберите пользовательскую историю одной из ваших функций.
- Затем реализуйте класс PageObject . PageObject — это шаблон, который моделирует элементы пользовательского интерфейса веб-приложения как объекты, поэтому тесты могут взаимодействовать с ними программно. Обратите внимание, что в этом случае мы кодируем «как» мы получаем доступ к HTML- странице.
- Следующим шагом является реализация библиотеки шагов. Этот класс будет содержать все шаги, необходимые для выполнения действия. Например, для создания новой книги необходимо открыть страницу addnewbook , вставить новые данные и нажать кнопку «Отправить». В этом случае мы кодируем «что» нам нужно для реализации критериев приемлемости.
- И, наконец, кодирование выбранной пользовательской истории в соответствии с определенными критериями принятия и использованием предыдущих классов шагов.
расширение JUnit, которое нацелено на управление жизненным циклом необходимого
движка NoSQL , помогает нам поддерживать базу данных в известном состоянии и стандартизировать способ написания тестов для
приложений NoSQL .
NoSQLUnit состоит из двух групп
правил JUnit и двух аннотаций. В текущем случае нам не нужно управлять жизненным циклом
движка NoSQL , потому что он управляется внешним объектом (
Openshift ).
Итак, давайте приступим к работе:
первое, что мы собираемся сделать, это создать класс объектов, который не содержит тестовый код; он используется как способ представления структуры требований.
public class Application { @Feature public class Books { public class ListAllBooks {} } }
Обратите внимание, что каждая реализованная функция должна содержаться в классе с
аннотацией @Feature . Каждый метод избранного класса представляет пользовательскую историю.
Следующим шагом является создание класса PageObject. Помните, что шаблон PageObject моделирует пользовательский интерфейс веб-приложения как объект. Итак, давайте посмотрим
html- файл, чтобы проверить, какие элементы должны отображаться.
<table id="listBooks" cellspacing="0" cellpadding="5"> <caption>List of Available Books</caption> <tr> <th>Title</th> <th>Number Of Pages</th> <th>Cover</th> </tr> ..... </table>
Самое главное здесь в том , что
таблица теги имеют
идентификаторы имени
listBooks , которые будут использоваться в классе PageObject , чтобы получить ссылку на его параметры и данные. Давайте напишем объект страницы:
@DefaultUrl("http://books-lordofthejars.rhcloud.com/GetAllBooks") public class FindAllBooksPage extends PageObject { @FindBy(id = "listBooks") private WebElement tableBooks; public FindAllBooksPage(WebDriver driver) { super(driver); } public TableWebElement getBooksTable() { Map<String, List<String>> tableValues = new HashMap<String, List<String>>(); tableValues.put("titles", titles()); tableValues.put("numberOfPages", numberOfPages()); tableValues.put("covers", coversUrl()); return new TableWebElement(tableValues); } private List<String> titles() { List<WebElement> namesWebElement = tableBooks.findElements(By.className("title")); return with(namesWebElement).convert(toStringValue()); } private List<String> numberOfPages() { List<WebElement> numberOfPagesWebElement = tableBooks.findElements(By.className("numberOfPages")); return with(numberOfPagesWebElement).convert(toStringValue()); } private List<String> coversUrl() { List<WebElement> coverUrlWebElement = tableBooks.findElements(By.className("cover")); return with(coverUrlWebElement).convert(toImageUrl()); } private Converter<WebElement, String> toImageUrl() { return new Converter<WebElement, String>() { @Override public String convert(WebElement from) { WebElement imgTag = from.findElement(By.tagName("img")); return imgTag.getAttribute("src"); } }; } private Converter<WebElement, String> toStringValue() { return new Converter<WebElement, String>() { @Override public String convert(WebElement from) { return from.getText(); } }; } }
Используя
@DefaultUrl, мы устанавливаем, какой URL отображается, с помощью
@FindBy мы отображаем веб-элемент с помощью id
listBooks и, наконец,
метод getBooksTable (), который возвращает содержимое сгенерированной
HTML- таблицы.
Следующее, что нужно сделать, это реализовать класс steps; в этом простом случае нам нужно всего два шага: первый, который открывает
страницу GetAllBooks , и другой, который утверждает, что таблица содержит ожидаемые элементы.
public class EndUserSteps extends ScenarioSteps { public EndUserSteps(Pages pages) { super(pages); } private static final long serialVersionUID = 1L; @Step public void should_obtain_all_inserted_books() { TableWebElement booksTable = onFindAllBooksPage().getBooksTable(); List<String> titles = booksTable.getColumn("titles"); assertThat(titles, hasItems("The Lord Of The Rings", "The Hobbit")); List<String> numberOfPages = booksTable.getColumn("numberOfPages"); assertThat(numberOfPages, hasItems("1299", "293")); List<String> covers = booksTable.getColumn("covers"); assertThat(covers, hasItems("http://upload.wikimedia.org/wikipedia/en/6/62/Jrrt_lotr_cover_design.jpg", "http://upload.wikimedia.org/wikipedia/en/4/4a/TheHobbit_FirstEdition.jpg")); } @Step public void open_find_all_page() { onFindAllBooksPage().open(); } private FindAllBooksPage onFindAllBooksPage() { return getPages().currentPageAt(FindAllBooksPage.class); } }
И, наконец, класс для проверки критериев приемлемости:
@Story(Application.Books.ListAllBooks.class) @RunWith(ThucydidesRunner.class) public class FindBooksStory { private final MongoDbConfiguration mongoDbConfiguration = mongoDb() .host("127.0.0.1").databaseName("books") .username(MongoDbConstants.USERNAME) .password(MongoDbConstants.PASSWORD).build(); @Rule public final MongoDbRule mongoDbRule = newMongoDbRule().configure( mongoDbConfiguration).build(); @Managed(uniqueSession = true) public WebDriver webdriver; @ManagedPages(defaultUrl = "http://books-lordofthejars.rhcloud.com") public Pages pages; @Steps public EndUserSteps endUserSteps; @Test @UsingDataSet(locations = "books.json", loadStrategy = LoadStrategyEnum.CLEAN_INSERT) public void finding_all_books_should_return_all_available_books() { endUserSteps.open_find_all_page(); endUserSteps.should_obtain_all_inserted_books(); } }
Есть несколько вещей, которые следует рассмотреть в предыдущем классе:
- @Story должен получить класс, определенный с помощью аннотации @Feature , чтобы Thucydides мог правильно создать отчет.
- Мы используем MongoDbRule для установления соединения с удаленным экземпляром MongoDb . Обратите внимание, что мы можем использовать адрес localhost из-за возможности переадресации портов Openshift , поэтому, хотя localhost используется, мы действительно управляем удаленным экземпляром MongoDb .
- Использование @Steps Thucydides создаст экземпляр библиотеки предыдущего шага.
- И, наконец, аннотация @UsingDataSet для заполнения данных в базе данных MongoDb перед запуском теста.
{ "book":[ { "title": "The Lord Of The Rings", "numberOfPages": "1299", "cover": "http:\/\/upload.wikimedia.org\/wikipedia\/en\/6\/62\/Jrrt_lotr_cover_design.jpg" }, { "title": "The Hobbit", "numberOfPages": "293", "cover": "http:\/\/upload.wikimedia.org\/wikipedia\/en\/4\/4a\/TheHobbit_FirstEdition.jpg" } ] }
Обратите внимание, что
NoSQLUnit поддерживает базу данных в известном состоянии, очищая базу данных перед каждым выполнением теста и заполняя ее известными данными, определенными в
файле json .
Также имейте в виду, что этот пример очень прост, поэтому было показано только небольшое подмножество возможностей
Thucydides и
NoSQLUnit . Следите за обоими сайтами:
http://thucydides.info и
https://github.com/lordofthejars/nosql-unit.
Мы продолжаем учиться,
Алекс.
Любовь — это горящая вещь, и она создает огненное кольцо, связанное диким желанием, я попадаю в кольцо огня (кольцо огня — Джонни Кэш)