为Openshift + MongoDb应用程序编写验收测试

验收测试用于确定是否满足规范要求。 它应该在与生产环境尽可能相似的环境中运行。 因此,如果您的应用程序已部署到Openshift中,则您将需要一个与生产环境中使用的帐户平行的帐户,以运行测试。 在这篇文章中,我们将为部署到Openshift的应用程序编写验收测试,该应用程序使用MongoDb作为数据库后端。

部署的应用程序是一个非常非常简单的库,它返回所有可借出的书。 该应用程序使用MongoDb来存储与书籍有关的所有信息。

因此,让我们开始描述先前应用程序的目标,功能,用户故事和接受标准。

目标 :扩大对大多数人的授课范围。
功能 :显示可用的书。
用户故事 :浏览目录->为了查找我想借的书,作为用户,我希望能够浏览所有书。 验收标准 :应该查看所有可用的书。

场景:

鉴于我想借一本书
当我在目录页面时
然后,我应该会看到可用的书籍信息:罐子之王– 1299 – LOTRCoverUrl,霍比特人– 293 – HobbitCoverUrl

注意,这是一个非常简单的应用程序,因此接受标准也很简单。

对于此示例,我们需要两个测试框架,第一个用于编写和运行验收测试,另一个用于管理NoSQL后端。 在这篇文章中,我们将使用修昔底德ATDDNoSQLUnit对付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>

这里最重要的是标记具有一个名为listBooksID ,将在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文件中的已知数据来填充数据库, 从而将数据库保持为已知状态。

还请记住,此示例非常简单,因此仅显示了ThucydidesNoSQLUnit功能的一小部分。 继续观看两个网站: 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/369785.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《大道至简》第四章读后感

流于形式的沟通 此章主要概括沟通的方式和方法决定着我们的成败&#xff0c;在软件开发的过程中必然存在着沟通交流&#xff0c;有效的沟通可以达到事半功倍的效果。 在项目开发时&#xff0c;我们肯定需要面对客户&#xff0c;客户的需求就是我们的工作方向&#xff0c;然而我…

CSS 自适应布局

前言 本篇文章将介页面布局中的自适应布局&#xff0c;常见的自适应布局有以下2种&#xff1a;左列固定右列自适应、左右两列固定中间自适应。 1. 左列固定右列自适应布局方案 说明&#xff1a;左列固定右列自适应&#xff0c;也可以为右列固定左列自适应&#xff0c;常见于中…

mysql的表导出er关系图_使用Navicat生成ER关系图并导出的方法

平时管理数据库一般都是用cmd命令提示符&#xff0c;或是IDEA Intellij自带的Data source&#xff0c;使用Navicat比较少。这段时间&#xff0c;由于要对前后端交互的数据结构进行设计&#xff0c;直接写文档联系多表时有些困难&#xff0c;想着如果有关系图就直观很多。想到Na…

pycaffe简明文档

pycaffe简明文档 by ChrisZZ, imzhuofoxmail.com 2018年01月18日19:00:56 说明 caffe的python接口没有官方说明文档&#xff0c;例如查看一个函数的用法&#xff0c;pytorch能查到所有的用法&#xff0c;而pycaffe则需要自行去查看源码。于是手动写了一个很粗糙的文档&#xff…

Java死锁示例–如何分析死锁情况

死锁是两个或多个线程永远被阻塞的编程情况&#xff0c;这种情况发生在至少两个线程和两个或更多资源的情况下。 在这里&#xff0c;我编写了一个简单的程序&#xff0c;该程序将导致死锁情况&#xff0c;然后我们将看到如何对其进行分析。 Java死锁示例 package com.journald…

项目

不解的问题&#xff1a;表格里td能否用margin&#xff1f;覆盖z-index需要设置背景才能覆盖&#xff1f;表格与表格能否用float&#xff1f; 转载于:https://www.cnblogs.com/ssx5310518/p/7282199.html

Scude导入MySQL_FM2017_FMF赛季更新和真实修正数据库[更新至9.9,超过89000个更新]

FM2017_FMF赛季更新和真实修正数据库[更新至9.9&#xff0c;超过89000个更新]FM2017_FMF赛季更新和真实修正数据库[更新至9.9&#xff0c;超过89000个更新](2)这是国外玩家制作的一款FM2017_FMF冬季更新和真实修正数据库&#xff0c;更新至9月9日&#xff0c;超过89000个更新内…

音视频和表单的详情

网页中的音视频 <audio> 和 <vedio> 标签属性&#xff1a;autoplay 自动播放 controls 控制播放 loop 循环播放 表单 HTML 表单用于收集用户输入。 标签<form> 标签属性 action 数据的路径 enctype 传输文件 enctype"multipart/form-data" method …

使用Jackson和Super类型令牌进行Json反序列化

Datatables是一个jquery插件&#xff0c;用于显示表格信息–它可以增强简单的表或可以使用基于AJAX的数据并以表格形式显示信息。 数据表要​​求来自服务器的数据遵循特定的JSON格式才能在屏幕上显示。 考虑要显示成员实体列表的情况&#xff0c;那么对于成员而言&#xff0c…

马拉车

O(n)求字符串中的最长回文串的长度 1 char s[SIZE];2 int len[SIZE*2];3 char str[SIZE*2];4 int manacher(){//预处理字符串&#xff0c;将字符串隔开,且开头和结尾字符串要不同,防止越界&#xff0c;如aaa预处理为#a#a#a$5 int l strlen(s);6 int ls 0;7 st…

mysql otter 数据同步_MySQL数据同步之otter

一、otter介绍基于日志数据&#xff0c;用于MySQL或者ORACLE之间准实时同步数据。用途&#xff1a;mysql/oracle互相同步中间表/行记录同步二、原理及架构图otter整体模块manager (提供web页面进行同步管理)arbitrate (分布式调度&#xff0c;可跨IDC机房)node (同步过程setl)c…

ubuntu中获取文件名称并生成txt文件

简介&#xff1a; 在机器视觉学习过程中&#xff0c;通常会经常批量处理一些图片&#xff0c;在&#xff35;&#xff42;&#xff55;&#xff4e;&#xff54;&#xff55;下可以使用find命令&#xff0c;来实现将文件名全部读取出来&#xff0c;生成列表txt文件&#xff0c;…

使用Google Guava创建收藏和实现不变性

因此&#xff0c;我想看看番石榴提供的一些集合创建模式&#xff0c;以及它提供的某些不可变集合类型。 如果您没有看过我以前的文章&#xff0c;则可能要从这里开始&#xff1a; 番石榴第1部分– MultiMaps 番石榴第2部分– BiMaps 番石榴第3部分–多组 Guava的所有集合实…

HTMLCSS

HTML xml &#xff08;标签名&#xff09;可扩展标记语言 <Stu> </Stu> Html 超文本标记语言&#xff08;文本&#xff0c;图片&#xff0c;链接&#xff09; <> </> Internet网上编写页面&#xff08;H5版本&#xff1a;支持多种标签特性&…

Mysql报错130_mysql 突然报错,连接不上

错误如下&#xff0c; Access denied for user rootlocalhost (using password关掉mysql服务&#xff0c;重新启动如果不行&#xff0c;那应该就是密码被改了&#xff0c;密码不对应1.以系统管理员身份运行cmd.2.查看mysql是否已经启动&#xff0c;如果已经启动&#xff0c;就停…

为内存密集型应用程序转义JVM堆

如果您曾经分配过大的Java堆&#xff0c;您就会知道在某个时候&#xff08;通常从大约4 GiB开始&#xff09;&#xff0c;您将开始遇到垃圾回收暂停的问题。 我不会详细介绍为什么在JVM中会出现暂停&#xff0c;但是总之&#xff0c;当JVM进行完整的收集并且您有很大的堆时&am…

MySQL学习笔记1(增删查改)

创建表&#xff1a; /*创建数据库create database 数据库名; */ CREATE DATABASE mybase; /*使用数据库use 数据库名 */ USE mybase;/*创建数据表的格式create table 表名(列名1 数据类型 约束,列名2 数据类型 约束,列名3 数据类型 约束);创建用户表,用户编号,姓名,用户的地址将…

Angular4 中内置指令的基本用法

ngFor 作用&#xff1a;像 for 循环一样&#xff0c;可以重复的从数组中取值并显示出来。 // .tsthis.userInfo [张三, 李四, 王五];// .html<div class"ui list" *ngFor"let username of userInfo"><div class"item">{{username}}…

初入编程的新世界

准备跨入程序员的行列了&#xff0c; 今天开课第一天&#xff0c; 算起来之前学习的几天&#xff0c; 第一次真正的了解了网页制作包括什么&#xff0c; html&#xff08;结构&#xff09;&#xff0c;css&#xff08;页面美化 层叠样式表&#xff09;&#xff0c;JavaScri…

java中i+=2什么意思_三分钟看懂Java中i++与++i的性能差别以及循环中如何使用

在Java中&#xff0c;自增是一种非常常见的操作&#xff0c;在自增中&#xff0c;有两种写法&#xff0c;一种是前缀自增(i)&#xff0c;一种是后缀自增(i)。这里主要简单介绍两种自增的差别。一、含义差别前缀自增和后缀自增是不同的。前缀自增(i)是从内存中加载i&#xff0c;…