Статьи

Написание приемочных тестов для приложений Openshift + MongoDb

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

Развернутое приложение — очень простая библиотека, которая возвращает все книги, доступные для кредитования. Это приложение использует MongoDb для хранения всей информации, связанной с книгами.

Итак, давайте начнем с описания цели, функции, пользовательской истории и критериев принятия для предыдущего приложения.

Цель : расширение лекции для большинства людей.
Особенность : Показать доступные книги.
История пользователя : Просмотр каталога -> Чтобы найти книги, которые я хотел бы позаимствовать, как пользователь, я хочу иметь возможность просматривать все книги.
Критерии приемки : должны видеть все доступные книги.

Сценарий:

Учитывая, что я хочу взять книгу
Когда я нахожусь на странице каталога
Тогда я должен увидеть доступную информацию о книгах: Повелитель Банок — 1299 — LOTRCoverUrl, Хоббит — 293 — HobbitCoverUrl

Обратите внимание, что это очень простое приложение, поэтому критерии принятия тоже просты.

Для этого примера нам понадобятся две тестовые среды: первая для написания и запуска приемочных тестов, а другая для управления бэкэндом NoSQL . В этом посте мы собираемся использовать Thucydides для ATDD и NoSQLUnit для работы с MongoDb .

Приложение уже развернуто в Openshift , и вы можете взглянуть на https://books-lordofthejars.rhcloud.com/GetAllBooks

Thucydides — это инструмент, разработанный для облегчения написания автоматизированных приемочных и регрессионных тестов.

Thucydides использует API WebDriver для доступа к элементам HTML- страницы. Но также помогает вам организовать ваши тесты и пользовательские истории с помощью конкретной модели программирования, создавать отчеты о выполненных тестах и, наконец, также измерять функциональное покрытие.

Чтобы написать приемочные тесты с Thucydides следующие шаги должны быть выполнены.

  • Прежде всего выберите пользовательскую историю одной из ваших функций.
  • Затем реализуйте класс PageObject . PageObject — это шаблон, который моделирует элементы пользовательского интерфейса веб-приложения как объекты, поэтому тесты могут взаимодействовать с ними программно. Обратите внимание, что в этом случае мы кодируем «как» мы получаем доступ к html- странице.
  • Следующим шагом является реализация библиотеки шагов. Этот класс будет содержать все шаги, необходимые для выполнения действия. Например, для создания новой книги необходимо открыть страницу addnewbook , вставить новые данные и нажать кнопку «Отправить». В этом случае мы кодируем «что» нам нужно для реализации критериев приемлемости.
  • И, наконец, кодирование выбранной пользовательской истории в соответствии с определенными критериями принятия и использованием предыдущих классов шагов.

NoSQLUnit — это расширение JUnit, которое нацелено на управление жизненным циклом необходимого движка NoSQL , помогает нам поддерживать базу данных в известном состоянии и стандартизировать способ написания тестов для приложений NoSQL .

NoSQLUnit состоит из двух групп правил JUnit и двух аннотаций. В текущем случае нам не нужно управлять жизненным циклом движка NoSQL , потому что он управляется внешним объектом ( Openshift ).

Итак, давайте приступим к работе:

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

1
2
3
4
5
6
7
8
public class Application {
 
    @Feature
    public class Books {
        public class ListAllBooks {}
    }
 
}

Обратите внимание, что каждая реализованная функция должна содержаться в классе с аннотацией @Feature . Каждый метод избранного класса представляет пользовательскую историю.

Следующим шагом является создание класса PageObject. Помните, что шаблон PageObject моделирует пользовательский интерфейс веб-приложения как объект. Итак, давайте посмотрим html- файл, чтобы проверить, какие элементы должны отображаться.

1
2
3
4
5
6
7
8
9
<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 для получения ссылки на его параметры и данные. Давайте напишем объект страницы:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
@DefaultUrl('http:books-lordofthejars.rhcloud.comGetAllBooks')
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 , и другой, который утверждает, что таблица содержит ожидаемые элементы.

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
27
28
29
30
31
32
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.orgwikipediaen662Jrrt_lotr_cover_design.jpg', 'http:upload.wikimedia.orgwikipediaen44aTheHobbit_FirstEdition.jpg'));
 }
 
 @Step
 public void open_find_all_page() {
  onFindAllBooksPage().open();
 }
 
 private FindAllBooksPage onFindAllBooksPage() {
  return getPages().currentPageAt(FindAllBooksPage.class);
 }
 
}

И, наконец, класс для проверки критериев приемлемости:

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
27
28
29
30
@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 перед запуском теста.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
{
 'book':[
  {
      'title': 'The Lord Of The Rings',
      'numberOfPages': '1299',
      'cover': 'http:upload.wikimedia.orgwikipediaen662Jrrt_lotr_cover_design.jpg'
  },
  {
   'title': 'The Hobbit',
   'numberOfPages': '293',
   'cover': 'http:upload.wikimedia.orgwikipediaen44aTheHobbit_FirstEdition.jpg'
  }
 ]
}

Обратите внимание, что NoSQLUnit поддерживает базу данных в известном состоянии, очищая базу данных перед каждым выполнением теста и заполняя ее известными данными, определенными в файле json .

Также имейте в виду, что этот пример очень прост, поэтому было показано только небольшое подмножество возможностей Thucydides и NoSQLUnit . Следите за обоими сайтами: http://thucydides.info и https://github.com/lordofthejars/nosql-unit.

Мы продолжаем учиться,
Алекс.

Ссылка: Написание приемочных тестов для приложений Openshift + MongoDb от нашего партнера JCG Алекса Сото в блоге One Jar To Rule All .