验收测试用于确定是否满足规范要求。 它应该在与生产环境尽可能相似的环境中运行。 因此,如果您的应用程序已部署到Openshift中,则您将需要一个与生产环境中使用的帐户平行的帐户,以运行测试。 在这篇文章中,我们将为部署到Openshift的应用程序编写验收测试,该应用程序使用MongoDb作为数据库后端。
部署的应用程序是一个非常非常简单的库,它返回所有可借出的书。 该应用程序使用MongoDb来存储与书籍有关的所有信息。
因此,让我们开始描述先前应用程序的目标,功能,用户故事和接受标准。
目标 :扩大对大多数人的授课范围。
功能 :显示可用的书。
用户故事 :浏览目录->为了查找我想借的书,作为用户,我希望能够浏览所有书。 验收标准 :应该查看所有可用的书。
场景:
鉴于我想借一本书
当我在目录页面时
然后,我应该会看到可用的书籍信息:罐子之王– 1299 – LOTRCoverUrl,霍比特人– 293 – HobbitCoverUrl
注意,这是一个非常简单的应用程序,因此接受标准也很简单。
对于此示例,我们需要两个测试框架,第一个用于编写和运行验收测试,另一个用于管理NoSQL后端。 在这篇文章中,我们将使用修昔底德的ATDD和NoSQLUnit对付MongoDB的 。
该应用程序已经部署在Openshift中 ,您可以查看https://books-lordofthejars.rhcloud.com/GetAllBooks
Thucydides是一种工具,旨在简化编写自动验收和回归测试的过程。
Thucydides使用WebDriver API访问HTML页面元素。 而且还可以帮助您通过使用具体的编程模型来组织测试和用户故事,创建已执行测试的报告,最后还可以测量功能覆盖率。
要用Thucydides编写验收测试,应遵循以下步骤。
- 首先,选择您的功能之一的用户故事。
- 然后实现PageObject类。 PageObject是一种将Web应用程序的用户界面元素建模为对象的模式,因此测试可以以编程方式与其交互。 请注意,在这种情况下,我们正在编码“如何”访问HTML页面。
- 下一步是实现步骤库。 此类将包含执行操作所需的所有步骤。 例如,创建新书需要打开addnewbook页面,插入新数据,然后单击提交按钮。 在这种情况下,我们正在编码“什么”来实现验收标准。
- 最后,按照定义的验收标准并使用先前的步骤类对选定的用户故事进行编码。
NoSQLUnit是一个JUnit扩展,旨在使我们能够管理所需的NoSQL引擎的生命周期,帮助我们将数据库维护为已知状态并简化为NoSQL应用程序编写测试的方式。
NoSQLUnit由两组JUnit规则和两个注释组成。 在当前情况下,我们不需要管理NoSQL引擎的生命周期,因为它是由外部实体( Openshift )管理的。
因此,让我们开始工作:
我们要做的第一件事是创建一个不包含测试代码的要素类。 它用作表示需求结构的一种方式。
public class Application {@Featurepublic class Books {public class ListAllBooks {}}}
请注意,每个实现的功能都应包含在带有@Feature注释的类中。 特色类的每种方法都代表一个用户故事。
下一步是创建PageObject类。 请记住,PageObject模式将Web应用程序的用户界面建模为对象。 因此,让我们看一下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的ID ,将在PageObject类中使用该ID以获得对其参数和数据的引用。 让我们编写页面对象:
@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>() {@Overridepublic 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>() {@Overridepublic String convert(WebElement from) {return from.getText();}};}}
使用@DefaultUrl我们设置要映射的URL,使用@FindBy我们映射ID为listBooks的Web元素,最后映射返回生成的html表内容的getBooksTable()方法。
接下来要做的是实现步骤类。 在这种简单的情况下,我们只需要两步,第一步打开GetAllBooks页面,另一步断言该表包含期望的元素。
public class EndUserSteps extends ScenarioSteps {public EndUserSteps(Pages pages) {super(pages);}private static final long serialVersionUID = 1L;@Steppublic 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'));}@Steppublic 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();@Rulepublic final MongoDbRule mongoDbRule = newMongoDbRule().configure(mongoDbConfiguration).build();@Managed(uniqueSession = true)public WebDriver webdriver;@ManagedPages(defaultUrl = 'http:books-lordofthejars.rhcloud.com')public Pages pages;@Stepspublic 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实例的连接。 请注意,由于端口转发具有Openshift功能,因此我们可以使用本地主机地址,因此尽管使用了本地主机,但实际上我们正在管理远程MongoDb实例。
- 使用@Steps Thucydides将创建先前步骤库的实例。
- 最后使用@UsingDataSet批注在运行测试之前将数据填充到MongoDb数据库中。
{'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
参考:来自我们的JCG合作伙伴 Alex Soto的Openshift + MongoDb应用程序的编写验收测试,位于One Jar To Rule All All博客上。
翻译自: https://www.javacodegeeks.com/2012/12/writing-acceptance-tests-for-openshift-mongodb-applications.html