51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

查看: 3046|回复: 1
打印 上一主题 下一主题

[讨论] 使用MongoDB和Spring数据进行集成测试

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2018-4-28 15:47:22 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
集成测试是企业发展中经常被忽视的领域。 这主要是由于为集成测试设置必要的基础架构的相关复杂性。 对于由
数据库支持的应用程序,为集成测试设置数据库,并且一旦测试完成(例如,数据文件,模式等),就需要相当复
杂和耗时,以确保测试的可重复性。 虽然已经有许多工具(例如DBUnit)和机制(例如测试后回滚)来辅助这一点
,但是固有的复杂性和问题总是存在的。
但是如果你使用MongoDB,有一个很酷和容易的方法来做你的单元测试,几乎简单的编写一个单元测试与嘲笑。
通过“EmbedMongo”,我们可以轻松地设置嵌入式MongoDB实例进行测试,一旦测试完成,内置的清理支持。 在
本文中,我们将演示一个示例,其中EmbedMongo与JUnit一起用于集成测试Repository实现。

上述设置的Maven POM看起来像这样。

  1. <?xml version="1.0" encoding="UTF-8"?>

  2. <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"

  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

  4. <modelVersion>4.0.0</modelVersion>

  5. <groupId>com.yohanliyanage.blog.mongoit</groupId>

  6. <artifactId>mongo-it</artifactId>

  7. <version>1.0</version>

  8. <dependencies>

  9. <dependency>

  10. <groupId>org.springframework.data</groupId>

  11. <artifactId>spring-data-mongodb</artifactId>

  12. <version>1.0.3.RELEASE</version>

  13. <scope>compile</scope>

  14. </dependency>

  15. <dependency>

  16. <groupId>junit</groupId>

  17. <artifactId>junit</artifactId>

  18. <version>4.10</version>

  19. <scope>test</scope>

  20. </dependency>

  21. <dependency>

  22. <groupId>org.springframework</groupId>

  23. <artifactId>spring-context</artifactId>

  24. <version>3.1.3.RELEASE</version>

  25. <scope>compile</scope>

  26. </dependency>

  27. <dependency>

  28. <groupId>de.flapdoodle.embed</groupId>

  29. <artifactId>de.flapdoodle.embed.mongo</artifactId>

  30. <version>1.26</version>

  31. <scope>test</scope>

  32. </dependency>

  33. </dependencies>

  34. </project>
  35. 或者如果你喜欢Gradle(顺便说一下,Gradle是一个真棒构建工具,你应该检查,如果你还没有这样做)。

  36. apply plugin: 'java'

  37. apply plugin: 'eclipse'

  38. sourceCompatibility = 1.6

  39. group = "com.yohanliyanage.blog.mongoit"

  40. version = '1.0'

  41. ext.springVersion = '3.1.3.RELEASE'

  42. ext.junitVersion = '4.10'

  43. ext.springMongoVersion = '1.0.3.RELEASE'

  44. ext.embedMongoVersion = '1.26'

  45. repositories {

  46. mavenCentral()

  47. maven { url 'http://repo.springsource.org/release' }

  48. }

  49. dependencies {

  50. compile "org.springframework:spring-context:${springVersion}"

  51. compile "org.springframework.data:spring-data-mongodb:${springMongoVersion}"

  52. testCompile "junit:junit:${junitVersion}"

  53. testCompile "de.flapdoodle.embed:de.flapdoodle.embed.mongo:${embedMongoVersion}"

  54. }
  55. 首先,这里是我们将存储在Mongo的文档。

  56. package com.yohanliyanage.blog.mongoit.model;

  57. import org.springframework.data.mongodb.core.index.Indexed;

  58. import org.springframework.data.mongodb.core.mapping.Document;

  59. /**

  60. * A Sample Document.

  61. *

  62. * @author Yohan Liyanage

  63. *

  64. */

  65. @Document

  66. public class Sample {

  67. @Indexed

  68. private String key;

  69. private String value;

  70. public Sample(String key, String value) {

  71. super();

  72. this.key = key;

  73. this.value = value;

  74. }

  75. public String getKey() {

  76. return key;

  77. }

  78. public void setKey(String key) {

  79. this.key = key;

  80. }

  81. public String getValue() {

  82. return value;

  83. }

  84. public void setValue(String value) {

  85. this.value = value;

  86. }

  87. }
  88. 为了帮助存储和管理这个文档,让我们写一个简单的Repository实现。 存储库接口如下。

  89. package com.yohanliyanage.blog.mongoit.repository;

  90. import java.util.List;

  91. import com.yohanliyanage.blog.mongoit.model.Sample;

  92. /**

  93. * Sample Repository API.

  94. *

  95. * @author Yohan Liyanage

  96. *

  97. */

  98. public interface SampleRepository {

  99. /**

  100. * Persists the given Sample.

  101. * @param sample

  102. */

  103. void save(Sample sample);

  104. /**

  105. * Returns the list of samples with given key.

  106. * @param sample

  107. * @return

  108. */

  109. List<Sample> findByKey(String key);

  110. }
  111. 和实现...

  112. package com.yohanliyanage.blog.mongoit.repository;

  113. import java.util.List;

  114. import static org.springframework.data.mongodb.core.query.Query.query;

  115. import static org.springframework.data.mongodb.core.query.Criteria.*;

  116. import org.springframework.beans.factory.annotation.Autowired;

  117. import org.springframework.data.mongodb.core.MongoOperations;

  118. import org.springframework.stereotype.Repository;

  119. import com.yohanliyanage.blog.mongoit.model.Sample;

  120. /**

  121. * Sample Repository MongoDB Implementation.

  122. *

  123. * @author Yohan Liyanage

  124. *

  125. */

  126. @Repository

  127. public class SampleRepositoryMongoImpl implements SampleRepository {

  128. @Autowired

  129. private MongoOperations mongoOps;

  130. /**

  131. * {@inheritDoc}

  132. */

  133. public void save(Sample sample) {

  134. mongoOps.save(sample);

  135. }

  136. /**

  137. * {@inheritDoc}

  138. */

  139. public List<Sample> findByKey(String key) {

  140. return mongoOps.find(query(where("key").is(key)), Sample.class);

  141. }

  142. /**

  143. * Sets the MongoOps implementation.

  144. *

  145. * @param mongoOps the mongoOps to set

  146. */

  147. public void setMongoOps(MongoOperations mongoOps) {

  148. this.mongoOps = mongoOps;

  149. }

  150. }
  151. 为了连接这个,我们需要一个Spring Bean配置。 注意,我们不需要这个测试。 但为了完成,我已经包括了这一点。 XML配置如下。

  152. <?xml version="1.0" encoding="UTF-8"?>

  153. <beans xmlns="http://www.springframework.org/schema/beans"

  154. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  155. xmlns:mongo="http://www.springframework.org/schema/data/mongo"

  156. xmlns:context="http://www.springframework.org/schema/context"

  157. xsi:schemaLocation="http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd

  158. http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd

  159. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">

  160. <!-- Enable Annotation Driven Configuration -->

  161. <context:annotation-config />

  162. <!-- Component Scan Packages for Annotation Driven Configuration -->

  163. <context:component-scan base-package="com.yohanliyanage.blog.mongoit.repository" />

  164. <!-- Mongo DB -->

  165. <mongo:mongo host="127.0.0.1" port="27017" />

  166. <!-- Mongo DB Factory -->

  167. <mongo:db-factory dbname="mongoit" mongo-ref="mongo"/>

  168. <!-- Mongo Template -->

  169. <bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">

  170. <constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />

  171. </bean>

  172. </beans>
复制代码




分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏
回复

使用道具 举报

该用户从未签到

2#
 楼主| 发表于 2018-4-28 15:47:35 | 只看该作者

现在我们已经准备好使用Embed Mongo编写我们的存储库实现的集成测试。
理想情况下,集成测试应该放在单独的源目录中,就像我们放置单元测试(例如src / test / java => src / inte
gration-test / java)。
然而,Maven和Gradle都是灵活的,所以你可以配置POM / build.gradle来处理这个。 然而,为了保持这个讨论
简单和集中,我将把集成测试放在'src / test / java',但我不推荐这个实际应用程序。
让我们开始编写集成测试。 首先,让我们从一个简单的基于JUnit的方法的Test开始。

package com.yohanliyanage.blog.mongoit.repository;

import static org.junit.Assert.fail;

import org.junit.After;

import org.junit.Before;

import org.junit.Test;

/**

* Integration Test for {@link SampleRepositoryMongoImpl}.

*

* @author Yohan Liyanage

*/

public class SampleRepositoryMongoImplIntegrationTest {

private SampleRepositoryMongoImpl repoImpl;

@Before

public void setUp() throws Exception {

repoImpl = new SampleRepositoryMongoImpl();

}

@After

public void tearDown() throws Exception {

}

@Test

public void testSave() {

fail("Not yet implemented");

}

@Test

public void testFindByKey() {

fail("Not yet implemented");

}

}
当这个JUnit测试用例初始化时,我们需要激活EmbedMongo来启动一个嵌入式Mongo服务器。 此外,当测试用例结束时,我们需要清理DB。 以下代码段执行此操作。

package com.yohanliyanage.blog.mongoit.repository;

import static org.junit.Assert.fail;

import java.io.IOException;

import org.junit.*;

import org.springframework.data.mongodb.core.MongoTemplate;

import com.mongodb.Mongo;

import com.yohanliyanage.blog.mongoit.model.Sample;

import de.flapdoodle.embed.mongo.MongodExecutable;

import de.flapdoodle.embed.mongo.MongodProcess;

import de.flapdoodle.embed.mongo.MongodStarter;

import de.flapdoodle.embed.mongo.config.MongodConfig;

import de.flapdoodle.embed.mongo.config.RuntimeConfig;

import de.flapdoodle.embed.mongo.distribution.Version;

import de.flapdoodle.embed.process.extract.UserTempNaming;

/**

* Integration Test for {@link SampleRepositoryMongoImpl}.

*

* @author Yohan Liyanage

*/

public class SampleRepositoryMongoImplIntegrationTest {

private static final String LOCALHOST = "127.0.0.1";

private static final String DB_NAME = "itest";

private static final int MONGO_TEST_PORT = 27028;

private SampleRepositoryMongoImpl repoImpl;

private static MongodProcess mongoProcess;

private static Mongo mongo;

private MongoTemplate template;

@BeforeClass

public static void initializeDB() throws IOException {

RuntimeConfig config = new RuntimeConfig();

config.setExecutableNaming(new UserTempNaming());

MongodStarter starter = MongodStarter.getInstance(config);

MongodExecutable mongoExecutable = starter.prepare(new MongodConfig(Version.V2_2_0, MONGO_TEST_PORT, false));

mongoProcess = mongoExecutable.start();

mongo = new Mongo(LOCALHOST, MONGO_TEST_PORT);

mongo.getDB(DB_NAME);

}

@AfterClass

public static void shutdownDB() throws InterruptedException {

mongo.close();

mongoProcess.stop();

}

@Before

public void setUp() throws Exception {

repoImpl = new SampleRepositoryMongoImpl();

template = new MongoTemplate(mongo, DB_NAME);

repoImpl.setMongoOps(template);

}

@After

public void tearDown() throws Exception {

template.dropCollection(Sample.class);

}

@Test

public void testSave() {

fail("Not yet implemented");

}

@Test

public void testFindByKey() {

fail("Not yet implemented");

}

}

initializeDB()方法用@BeforeClass注释,以在测试用例之前启动它。 此方法触发绑定到给定端口的嵌入式Mon
goDB实例,并暴露设置为使用给定数据库的Mongo对象。 在内部,EmbedMongo在临时目录中创建必要的数
据文件。
当这个方法第一次执行时,EmbedMongo将下载必要的Mongo实现(如上面代码中的Version.V2_2_0所示),
如果它不存在的话。 这是一个不错的设施,特别是当谈到连续集成服务器。 您不必在每个CI服务器中手动设
置Mongo。 这是测试的一个外部依赖。
在用@AfterClass注释的shutdownDB()方法中,我们停止了EmbedMongo进程。 这会在EmbedMongo中触
发必要的清除操作,以删除临时数据文件,将状态恢复到执行测试用例之前的状态。
我们现在更新了setUp()方法来构建Spring MongoTemplate对象,该对象由EmbedMongo公开的Mongo实例
支持,并使用该模板设置我们的RepoImpl。 tearDown()方法被更新以删除“Sample”集合,以确保我们的每
个测试方法都以clean状态开始。
现在只是写实际测试方法的问题。
让我们从保存方法测试开始。

@Test

public void testSave() {

Sample sample = new Sample("TEST", "2");

repoImpl.save(sample);

int samplesInCollection = template.findAll(Sample.class).size();

assertEquals("Only 1 Sample should exist collection, but there are "

+ samplesInCollection, 1, samplesInCollection);

}
我们创建一个Sample对象,将它传递给repoImpl.save(),并断言以确保Sample集合中只有一个Sample。简单,直接的东西。
下面是findByKey方法的测试方法。

@Test

public void testFindByKey() {

// Setup Test Data

List<Sample> samples = Arrays.asList(

new Sample("TEST", "1"), new Sample("TEST", "25"),

new Sample("TEST2", "66"), new Sample("TEST2", "99"));

for (Sample sample : samples) {

template.save(sample);

}

// Execute Test

List<Sample> matches = repoImpl.findByKey("TEST");

// Note: Since our test data (populateDummies) have only 2

// records with key "TEST", this should be 2

assertEquals("Expected only two samples with key TEST, but there are "

+ matches.size(), 2, matches.size());

}
最初,我们通过将一组Sample对象添加到数据存储中来设置数据。 重要的是我们在这里直接使用template.save(),因为repoImpl.save()是一个被测试的方法。 我们不是在这里测试,所以我们在数据设置过程中使用底层的“可信”template.save()。 这是单元/集成测试中的基本概念。 然后我们执行test'findByKey'下的方法,并断言以确保只有两个样本匹配我们的查询。
同样,我们可以继续为每个存储库方法编写更多的测试,包括负测试。 这里是最终的集成测试文件。

package com.yohanliyanage.blog.mongoit.repository;

import static org.junit.Assert.*;

import java.io.IOException;

import java.util.Arrays;

import java.util.List;

import org.junit.*;

import org.springframework.data.mongodb.core.MongoTemplate;

import com.mongodb.Mongo;

import com.yohanliyanage.blog.mongoit.model.Sample;

import de.flapdoodle.embed.mongo.MongodExecutable;

import de.flapdoodle.embed.mongo.MongodProcess;

import de.flapdoodle.embed.mongo.MongodStarter;

import de.flapdoodle.embed.mongo.config.MongodConfig;

import de.flapdoodle.embed.mongo.config.RuntimeConfig;

import de.flapdoodle.embed.mongo.distribution.Version;

import de.flapdoodle.embed.process.extract.UserTempNaming;

/**

* Integration Test for {@link SampleRepositoryMongoImpl}.

*

* @author Yohan Liyanage

*/

public class SampleRepositoryMongoImplIntegrationTest {

private static final String LOCALHOST = "127.0.0.1";

private static final String DB_NAME = "itest";

private static final int MONGO_TEST_PORT = 27028;

private SampleRepositoryMongoImpl repoImpl;

private static MongodProcess mongoProcess;

private static Mongo mongo;

private MongoTemplate template;

@BeforeClass

public static void initializeDB() throws IOException {

RuntimeConfig config = new RuntimeConfig();

config.setExecutableNaming(new UserTempNaming());

MongodStarter starter = MongodStarter.getInstance(config);

MongodExecutable mongoExecutable = starter.prepare(new MongodConfig(Version.V2_2_0, MONGO_TEST_PORT, false));

mongoProcess = mongoExecutable.start();

mongo = new Mongo(LOCALHOST, MONGO_TEST_PORT);

mongo.getDB(DB_NAME);

}

@AfterClass

public static void shutdownDB() throws InterruptedException {

mongo.close();

mongoProcess.stop();

}

@Before

public void setUp() throws Exception {

repoImpl = new SampleRepositoryMongoImpl();

template = new MongoTemplate(mongo, DB_NAME);

repoImpl.setMongoOps(template);

}

@After

public void tearDown() throws Exception {

template.dropCollection(Sample.class);

}

@Test

public void testSave() {

Sample sample = new Sample("TEST", "2");

repoImpl.save(sample);

int samplesInCollection = template.findAll(Sample.class).size();

assertEquals("Only 1 Sample should exist in collection, but there are "

+ samplesInCollection, 1, samplesInCollection);

}

@Test

public void testFindByKey() {

// Setup Test Data

List<Sample> samples = Arrays.asList(

new Sample("TEST", "1"), new Sample("TEST", "25"),

new Sample("TEST2", "66"), new Sample("TEST2", "99"));

for (Sample sample : samples) {

template.save(sample);

}

// Execute Test

List<Sample> matches = repoImpl.findByKey("TEST");

// Note: Since our test data (populateDummies) have only 2

// records with key "TEST", this should be 2

assertEquals("Expected only two samples with key TEST, but there are "

+ matches.size(), 2, matches.size());

}

}

另一方面,集成测试的一个关键问题是执行时间。 我们都希望保持我们的测试执行时间尽可能低,最好是几秒钟,
以确保我们可以在CI期间运行所有测试,最小的构建和验证时间。 但是,由于集成测试依赖于底层基础结构,因此
通常集成测试需要时间运行。 但是使用EmbedMongo,情况并非如此。 在我的机器中,上面的测试套件在1.8秒内
运行,每种测试方法最多只有0.166秒。
回复 支持 反对

使用道具 举报

本版积分规则

关闭

站长推荐上一条 /1 下一条

小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

GMT+8, 2024-5-18 21:16 , Processed in 0.066732 second(s), 22 queries .

Powered by Discuz! X3.2

© 2001-2024 Comsenz Inc.

快速回复 返回顶部 返回列表