项目学生:分片集成测试数据

这是Project Student的一部分。 其他职位包括带有Jersey的 Web服务 客户端,带有Jersey的 Web服务服务器 , 业务层和带有Spring Data的持久性 。

到目前为止,所有集成测试都使用了内存嵌入式数据库,该数据库无法一次又一次地保留信息。 当我们将REST服务器与“真实”数据库服务器完全集成时,这种情况会发生变化-剩余的测试数据将污染我们的开发或测试数据库。 一旦我们进行了运行集成测试代码的连续集成,这将是一个非常头疼的事情。

一种解决方案是以一种允许我们的测试使用共享开发数据库而不污染它或其他测试的方式“分片”我们的集成测试数据。 最简单的方法是将TestRun字段添加到所有对象。 “测试”数据将具有指示特定测试运行的值,“实时”数据将具有空值。

确切的时间表是

  1. 创建并持久保存一个TestRun对象
  2. 创建具有适当TestRun值的测试对象
  3. 执行集成测试
  4. 删除测试对象
  5. 删除TestRun对象

TestRun表中的​​任何条目将是1)主动集成测试或2)引发未处理异常的集成测试失败(当然,这取决于事务管理器)。 重要的是要注意,即使事务管理器执行了回滚,我们也可以在引发意外异常后捕获数据库状态-这是对junit测试运行程序的简单扩展。

时间戳记和用户字段使您可以轻松地根据其年龄(例如,超过7天的任何测试)或运行测试的人员删除陈旧的测试数据。

TestablePersistentObject抽象基类

此更改从持久性级别开始,因此我们应该从持久性级别开始并逐步进行扩展。

我们首先用测试运行值扩展PersistentObject抽象基类。

@MappedSuperclass
public abstract class TestablePersistentObject extends PersistentObject {private static final long serialVersionUID = 1L;private TestRun testRun;/*** Fetch testRun object. We use lazy fetching since we rarely care about the* contents of this object - we just want to ensure referential integrity to* an existing testRun object when persisting a TPO.* * @return*/@ManyToOne(fetch = FetchType.LAZY, optional = true)public TestRun getTestRun() {return testRun;}public void setTestRun(TestRun testRun) {this.testRun = testRun;}@Transientpublic boolean isTestData() {return testRun != null;}
}

TestRun类

TestRun类包含有关单个集成测试运行的标识信息。 它包含一个名称(默认情况下,该集成测试为classname#methodname()) ,测试的日期和时间以及运行该测试的用户的名称。 捕获其他信息将很容易。

测试对象列表为我们带来了两个重大胜利。 首先,如果需要(例如,在意外的异常之后),可以轻松捕获数据库的状态。 其次,级联删除使删除所有测试对象变得容易。

@XmlRootElement
@Entity
@Table(name = "test_run")
@AttributeOverride(name = "id", column = @Column(name = "test_run_pkey"))
public class TestRun extends PersistentObject {private static final long serialVersionUID = 1L;private String name;private Date testDate;private String user;private List<TestablePersistentObject> objects = Collections.emptyList();@Column(length = 80, unique = false, updatable = true)public String getName() {return name;}public void setName(String name) {this.name = name;}@Column(name = "test_date", nullable = false, updatable = false)@Temporal(TemporalType.TIMESTAMP)public Date getTestDate() {return testDate;}public void setTestDate(Date testDate) {this.testDate = testDate;}@Column(length = 40, unique = false, updatable = false)public String getUser() {return user;}public void setUser(String user) {this.user = user;}@OneToMany(cascade = CascadeType.ALL)public List<TestablePersistentObject> getObjects() {return objects;}public void setObjects(List<TestablePersistentObject> objects) {this.objects = objects;}/*** This is similar to standard prepersist method but we also set default* values for everything else.*/@PrePersistpublic void prepersist() {if (getCreationDate() == null) {setCreationDate(new Date());}if (getTestDate() == null) {setTestDate(new Date());}if (getUuid() == null) {setUuid(UUID.randomUUID().toString());}if (getUser() == null) {setUser(System.getProperty("user.name"));}if (name == null) {setName("test run " + getUuid());}}
}

TestRun类扩展了PersistentObject,而不是TestablePersistentObject,因为我们的其他集成测试将充分利用它。

Spring数据仓库

我们必须向每个存储库添加一种其他方法。

@Repository
public interface CourseRepository extends JpaRepository {List<Course> findCoursesByTestRun(TestRun testRun);....
}

服务介面

同样,我们必须为每个服务添加两个其他方法。

public interface CourseService {List<Course> findAllCourses();Course findCourseById(Integer id);Course findCourseByUuid(String uuid);Course createCourse(String name);Course updateCourse(Course course, String name);void deleteCourse(String uuid);// new method for testingCourse createCourseForTesting(String name, TestRun testRun);// new method for testingList<Course> findAllCoursesForTestRun(TestRun testRun);
}

我不会显示TestRunRepository,TestRunService接口或TestRunService实现,因为它们与我在前几篇博客文章中所描述的相同。

服务实施

我们必须对现有Service实施进行一次小的更改,并添加两种新方法。

@Service
public class CourseServiceImpl implements CourseService {@Resourceprivate TestRunService testRunService;/*** @see com.invariantproperties.sandbox.student.business.CourseService#*      findAllCourses()*/@Transactional(readOnly = true)@Overridepublic List<Course> findAllCourses() {List<Course> courses = null;try {courses = courseRepository.findCoursesByTestRun(null);} catch (DataAccessException e) {if (!(e instanceof UnitTestException)) {log.info("error loading list of courses: " + e.getMessage(), e);}throw new PersistenceException("unable to get list of courses.", e);}return courses;}/*** @see com.invariantproperties.sandbox.student.business.CourseService#*      findAllCoursesForTestRun(com.invariantproperties.sandbox.student.common.TestRun)*/@Transactional(readOnly = true)@Overridepublic List<Course> findAllCoursesForTestRun(TestRun testRun) {List<Course> courses = null;try {courses = courseRepository.findCoursesByTestRun(testRun);} catch (DataAccessException e) {if (!(e instanceof UnitTestException)) {log.info("error loading list of courses: " + e.getMessage(), e);}throw new PersistenceException("unable to get list of courses.", e);}return courses;}/*** @see com.invariantproperties.sandbox.student.business.CourseService#*      createCourseForTesting(java.lang.String,*      com.invariantproperties.sandbox.student.common.TestRun)*/@Transactional@Overridepublic Course createCourseForTesting(String name, TestRun testRun) {final Course course = new Course();course.setName(name);course.setTestUuid(testRun.getTestUuid());Course actual = null;try {actual = courseRepository.saveAndFlush(course);} catch (DataAccessException e) {if (!(e instanceof UnitTestException)) {log.info("internal error retrieving course: " + name, e);}throw new PersistenceException("unable to create course", e);}return actual;}
}

CourseServiceIntegrationTest

我们对集成测试进行了一些更改。 我们只需更改一种测试方法,因为它是唯一实际创建测试对象的方法。 其余方法是不需要测试数据的查询。

请注意,我们更改名称值以确保其唯一性。 这是解决唯一性约束(例如,电子邮件地址)的一种方法。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { BusinessApplicationContext.class, TestBusinessApplicationContext.class,TestPersistenceJpaConfig.class })
@Transactional
@TransactionConfiguration(defaultRollback = true)
public class CourseServiceIntegrationTest {@Resourceprivate CourseService dao;@Resourceprivate TestRunService testService;@Testpublic void testCourseLifecycle() throws Exception {final TestRun testRun = testService.createTestRun();final String name = "Calculus 101 : " + testRun.getUuid();final Course expected = new Course();expected.setName(name);assertNull(expected.getId());// create courseCourse actual = dao.createCourseForTesting(name, testRun);expected.setId(actual.getId());expected.setUuid(actual.getUuid());expected.setCreationDate(actual.getCreationDate());assertThat(expected, equalTo(actual));assertNotNull(actual.getUuid());assertNotNull(actual.getCreationDate());// get course by idactual = dao.findCourseById(expected.getId());assertThat(expected, equalTo(actual));// get course by uuidactual = dao.findCourseByUuid(expected.getUuid());assertThat(expected, equalTo(actual));// get all coursesfinal List<Course> courses = dao.findCoursesByTestRun(testRun);assertTrue(courses.contains(actual));// update courseexpected.setName("Calculus 102 : " + testRun.getUuid());actual = dao.updateCourse(actual, expected.getName());assertThat(expected, equalTo(actual));// verify testRun.getObjectsfinal List<TestablePersistentObject> objects = testRun.getObjects();assertTrue(objects.contains(actual));// delete Coursedao.deleteCourse(expected.getUuid());try {dao.findCourseByUuid(expected.getUuid());fail("exception expected");} catch (ObjectNotFoundException e) {// expected}testService.deleteTestRun(testRun.getUuid());}....
}

我们可以使用@Before@After透明地包装所有测试方法,但是许多测试不需要测试数据,而许多需要测试数据的测试则需要唯一的测试数据,例如,电子邮件地址。 在后一种情况下,我们按照上述方法折叠测试UUID。

REST Web服务服务器

REST Web服务需要在请求类中添加测试uuid,并在创建对象时添加一些逻辑以正确处理它。

REST Web服务不支持获取所有测试对象的列表。 “正确”的方法将是创建TestRun服务并响应/ get / {id}查询提供关联的对象。

@XmlRootElement
public class Name {private String name;private String testUuid;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getTestUuid() {return testUuid;}public void setTestUuid(String testUuid) {this.testUuid = testUuid;}
}

现在,我们可以检查可选的testUuid字段并调用适当的create方法。

@Service
@Path("/course")
public class CourseResource extends AbstractResource {@Resourceprivate CourseService service;@Resourceprivate TestRunService testRunService;/*** Create a Course.* * @param req* @return*/@POST@Consumes({ MediaType.APPLICATION_JSON, MediaType.TEXT_XML })@Produces({ MediaType.APPLICATION_JSON, MediaType.TEXT_XML })public Response createCourse(Name req) {log.debug("CourseResource: createCourse()");final String name = req.getName();if ((name == null) || name.isEmpty()) {return Response.status(Status.BAD_REQUEST).entity("'name' is required'").build();}Response response = null;try {Course course = null;if (req.getTestUuid() != null) {TestRun testRun = testRunService.findTestRunByUuid(req.getTestUuid());if (testRun != null) {course = service.createCourseForTesting(name, testRun);} else {response = Response.status(Status.BAD_REQUEST).entity("unknown test UUID").build();}} else {course = service.createCourse(name);}if (course == null) {response = Response.status(Status.INTERNAL_SERVER_ERROR).build();} else {response = Response.created(URI.create(course.getUuid())).entity(scrubCourse(course)).build();}} catch (Exception e) {if (!(e instanceof UnitTestException)) {log.info("unhandled exception", e);}response = Response.status(Status.INTERNAL_SERVER_ERROR).build();}return response;}....
}

REST Web服务客户端

最后,REST服务器必须添加一种其他方法。 客户端尚不支持获取所有测试对象的列表。

public interface CourseRestClient {/*** Create specific course for testing.* * @param name* @param testRun*/Course createCourseForTesting(String name, TestRun testRun);....
}

public class CourseRestClientImpl extends AbstractRestClientImpl implements CourseRestClient {/*** Create JSON string.* * @param name* @return*/String createJson(final String name, final TestRun testRun) {return String.format("{ \"name\": \"%s\", \"testUuid\": \"%s\" }", name, testRun.getTestUuid());}/*** @see com.invariantproperties.sandbox.student.webservice.client.CourseRestClient#createCourse(java.lang.String)*/@Overridepublic Course createCourseForTesting(final String name, final TestRun testRun) {if (name == null || name.isEmpty()) {throw new IllegalArgumentException("'name' is required");}if (testRun == null || testRun.getTestUuid() == null || testRun.getTestUuid().isEmpty()) {throw new IllegalArgumentException("'testRun' is required");}return createObject(createJson(name, testRun));}....
}

源代码

可从http://code.google.com/p/invariant-properties-blog/source/browse/student获取源代码。

澄清度

我认为在TestRun中不可能有@OneToMany到TestablePersistentObject,但是使用H2的集成测试成功了。 不幸的是,当我使用PostgreSQL数据库启动完全集成的Web服务时,这会引起问题。 我将代码留在上面,因为即使我们没有通用集合,也总是可以有一个教室列表,一个课程列表等。 但是,代码已从源代码控制的版本中删除。

更正

接口方法应该是findCourseByTestRun_Uuid() ,而不是findCourseByTestRun() 。 另一种方法是使用JPA标准查询–请参阅“ 项目学生:JPA标准查询” 。

参考: 项目学生:来自Invariant Properties博客的JCG合作伙伴 Bear Giles提供的分片集成测试数据 。

翻译自: https://www.javacodegeeks.com/2014/01/project-student-sharding-integration-test-data.html

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

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

相关文章

BZOJ1036 树的统计(树链剖分+线段树)

【题目描述】 一棵树上有n个节点&#xff0c;编号分别为1到n&#xff0c;每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成一些操作&#xff1a; I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 III. QSUM u v: 询问…

Unity插件Gaia使用介绍

零基础创建Unity精美场景地形&#xff08;使用插件Gaia&#xff09;一、先上最终效果图二、软件环境搭建1.Unity5.6.0下载链接https://unity3d.com/cn/get-unity/download/archive?_ga2.110664517.1175563345.1516068066-173539005.15020707552.Gaia Unity地形制作插件下载链…

java http 压缩_解压HTTP API的GZIP压缩数据

1.对Java后端的请求HttpURLConnection对象中的消息头设置压缩connection.setRequestProperty("Accept-Encoding", "gzip, deflate");2.发送请求后获取response中的content-encodingconnection.getContentEncoding(); // 获取content-encoding3.如果content…

20151208_使用windows2012配置weblogic节点管理器

经过实践&#xff0c;weblogic节点管理器的作用主要有两点&#xff1a; 1、可通过weblogic控制台远程控制被管server启停。 2、可以自动重启被管server的进程&#xff0c;并且对spring框架提供比直接启动更快的重启速度。 配置步骤&#xff1a; 在管理电脑上&#xff1a; …

python各种包安装顺序_史上最全的Python包管理工具:Anaconda教程

事实上Anaconda 和 Jupyter notebook已成为数据分析的标准环境。简单来说&#xff0c;Anaconda是包管理器和环境管理器&#xff0c;Jupyter notebook 可以将数据分析的代码、图像和文档全部组合到一个web文档中。接下来我详细介绍下Anaconda&#xff0c;并在最后给出Jupyter no…

在Spring MVC中使用多个属性文件

每个人都听说过将单个Web应用程序组合成一个大型应用程序的门户。 门户软件的工作原理类似于mashup &#xff0d;来自多个来源的内容在单个服务中被拾取&#xff0c;大部分显示在单个网页中。 门户软件还允许在嵌入到门户软件中的所有单个Web应用程序&#xff08;独立模块&…

汇编语言实验一

实验任务 &#xff08;1&#xff09;使用debug&#xff0c;将程序段写入内存&#xff0c;逐条执行&#xff0c;观察cpu中相关寄存器内容变化。 完成此实验&#xff0c;可用e命令或a命令。 e命令改写内存的内容&#xff0c;如图&#xff1a; 我没有一气喝成&#xff0c;一开始便…

python学习 day6 (3月7日)

#__author : liuyang #date : 2019/3/7 0007 a [a , b , c] b [] print(a is b ) # 空元组 可以 空列表 不可以 print(tuple(a))题目&#xff1a; l1 [11, 22, 33, 44, 55] #将此列表索引为奇数的对应元素全部删除 # 错误示例 for l in range(len(l1)):print(l)if l % 2…

java jni helloword_JNI入门教程之HelloWorld篇

JNI入门教程之HelloWorld篇来源:互联网 宽屏版 评论2008-05-31 09:07:11本文讲述如何使用JNI技术实现HelloWorld&#xff0c;目的是让读者熟悉JNI的机制并编写第一个HelloWorld程序。java Native Interface(JNI)是Java语言的本地编程接口&#xff0c;是J2SDK的一部分。在java…

centos7 python3安装numpy_centos 7 下安装numpy、scipy等python包

本文适用于刚入门的小白&#xff0c;欢迎大牛们批评指正。因为要开始数据分析&#xff0c;而python又不像R和matlab那么简洁。需要安装的包很多~网上找了好多牛人博客&#xff0c;想在centos7下安装numpy&#xff0c;scipy等包&#xff0c;一开始就懵逼了&#xff0c;网上的指导…

11、Node.js 函数

内容&#xff1a;普通函数&#xff0c;匿名函数&#xff0c;函数传递是如何让HTTP服务器工作的 ###普通函数例子&#xff1a; function say(word) {console.log(word); } function execute(someFunction, value) {someFunction(value); } execute(say, "Hello"); ###…

休眠事实:集成测试策略

我喜欢集成测试&#xff0c;这是检查Hibernate生成哪些幕后花絮的SQL查询的好方法。 但是集成测试需要运行的数据库服务器&#xff0c;这是您必须要做的第一选择。 1.使用类似生产的本地数据库服务器进行集成测试 对于生产环境&#xff0c;我始终喜欢使用增量DDL脚本&#xff…

select多查询,自连接,join 等

题目来源于leetcode中的数据库部分&#xff1a;181. Employees Earning More Than Their Managers 题目&#xff1a;The Employee table holds all employees including their managers. Every employee has an Id, and there is also a column for the manager Id. ----------…

4、Node.js REPL(交互式解释器)

Node.js REPL(Read Eval Print Loop:交互式解释器) 表示一个电脑的环境&#xff0c;类似 Window 系统的终端或 Unix/Linux shell&#xff0c;我们可以在终端中输入命令&#xff0c;并接收系统的响应。Node 自带了交互式解释器&#xff0c;可以执行以下任务&#xff1a; • 读…

有时候eclipse 导入maven项目 启动的时候回出现这样一个问题

严重: A child container failed during start java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/SpringMvcController]]at java.util.conc…

公众平台模板消息所在行业_第三方工具微信公众号模板消息群发如何操作?

当下&#xff0c;公众平台模板消息功能仅支持添加模板&#xff0c;修改所在行业&#xff0c;如果想要群发模板消息&#xff0c;可以自己根据公众平台的接口编程实现&#xff0c;也可通过微号帮平台的模板消息群发功能实现&#xff0c;均可以让微信公众号群发模板消息&#xff0…

java spark 读取json_apache-spark - 与aws-java-sdk链接时,在读取json文件时发生Spark崩溃 - 堆栈内存溢出...

让config.json成为一个小的json文件&#xff1a;{"toto": 1}我做了一个简单的代码&#xff0c;用sc.textFile读取json文件(因为文件可以在S3&#xff0c;本地或HDFS上&#xff0c;所以textFile很方便)import org.apache.spark.{SparkContext, SparkConf}object testA…

项目学生:带有Jersey的Web服务服务器

这是Project Student的一部分。 其他职位包括带有Jersey的Webservice Client &#xff0c; 业务层和带有Spring Data的持久性 。 RESTful Web应用程序洋葱的第二层是Web服务服务器。 它应该是一个薄层&#xff0c;用于包装对业务层的调用&#xff0c;但不对其自身进行大量处理…

在 Snoop 中使用 PowerShell 脚本进行更高级的 UI 调试

在 Snoop 中使用 PowerShell 脚本进行更高级的 UI 调试 原文:在 Snoop 中使用 PowerShell 脚本进行更高级的 UI 调试版权声明&#xff1a;本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布&#xff0c;但务必保留文章署名…

java 通道 双向原理_Java-NIO(四):通道(Channel)的原理与获取

通道(Channel)&#xff1a;由java.nio.channels包定义的&#xff0c;Channel表示IO源与目标打开的连接&#xff0c;Channel类似于传统的“流”&#xff0c;只不过Channel本身不能直接访问数据&#xff0c;Channel只能与Buffer进行交互。通道主要用于传输数据&#xff0c;从缓冲…