Java 使用mongodb存储库进行单元测试
我已经构建了使用MongoDB的应用程序,并且在测试中遇到了问题 只要我使用JPA和一些关系数据库,我就使用一些测试层,将持久性切换到内存数据库(linke HSQLDB或MySQL)进行测试。这样我就可以限制IO操作并加快测试速度。 然而,对于MongoDB和Spring数据,使用基于扩展MongoRepository接口的存储库非常方便 我的问题是在使用存储库时如何处理单元测试和功能测试?例如,我有一个简单的类,它被命名为mongo文档:Java 使用mongodb存储库进行单元测试,java,spring,mongodb,unit-testing,spring-data-mongodb,Java,Spring,Mongodb,Unit Testing,Spring Data Mongodb,我已经构建了使用MongoDB的应用程序,并且在测试中遇到了问题 只要我使用JPA和一些关系数据库,我就使用一些测试层,将持久性切换到内存数据库(linke HSQLDB或MySQL)进行测试。这样我就可以限制IO操作并加快测试速度。 然而,对于MongoDB和Spring数据,使用基于扩展MongoRepository接口的存储库非常方便 我的问题是在使用存储库时如何处理单元测试和功能测试?例如,我有一个简单的类,它被命名为mongo文档: public class Company { @I
public class Company {
@Id
private String id;
@NotEmpty
private String name;
private String description;
private String website;
private String logo;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getWebsite() {
return website;
}
public void setWebsite(String website) {
this.website = website;
}
public String getLogo() {
return logo;
}
public void setLogo(String logo) {
this.logo = logo;
}
}
和相关存储库:
@Repository
public interface CompanyRepository extends MongoRepository<Company, Serializable> {
}
最后,我做了两个测试(不过可能是一个同时包含两个任务的测试),涵盖了控制器api和数据格式(在这里我模拟存储库以防止IO操作,效果很好),第二个测试我想确保传递的对象成功持久化。事实并非如此简单。首先(如果我错了请纠正我),内存中没有mongo实现。其次,我不能用mockito验证save方法的执行,因为我认为是继承
@ContextConfiguration(classes = Application.class)
@WebAppConfiguration
public class CompanyControllerNGTest extends AbstractTestNGSpringContextTests {
@Mock
private CompanyRepository repositoryMock;
@Autowired
@InjectMocks
private CompanyController controller;
private MockMvc mockMvc;
@BeforeMethod
public void setUp() {
MockitoAnnotations.initMocks(this);
}
@DataProvider
public static Object[][] companyJsonProvider() {
return new Object[][]{
{Json.createObjectBuilder()
.add("name", "JakasFirma")
.add("description", "jakas firma opis")
.add("website", "www.jakasfirma.com")
.add("logo", "jakies logo")
.build(), status().isOk()},
{Json.createObjectBuilder()
.add("name", "JakasFirma")
.build(), status().isOk()},
{Json.createObjectBuilder()
.add("description", "jakas firma opis")
.add("website", "www.jakasfirma.com")
.add("logo", "jakies logo")
.build(), status().isBadRequest()},
{Json.createObjectBuilder()
.build(), status().isBadRequest()},
};
}
@Test(dataProvider = "companyJsonProvider", enabled = false)
public void apiTest(JsonObject companyJson, ResultMatcher expectedStatus) throws Exception {
//given
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
String content = companyJson.toString();
//when
mockMvc.perform(post("/company").contentType(MediaType.APPLICATION_JSON).content(content)).
//then
andExpect(expectedStatus);
}
@Test
public void shouldCreate() throws Exception {
//given
//when
controller.create(mock(Company.class));
//then
//verify(repositoryMock, times(1)).save(any(Iterable.class));
}
}
我考虑在控制器和存储库之间引入一个dao层,可以对其进行模拟和验证。它增加了封装存储库使用的每个方法的复杂性和强制力。此外,它不会修复问题,但会部分地将其移动到较低级别。
是否有任何方法或实践可以帮助处理此类问题?或者我应该对mongo使用不同的方法?对于单元测试,我会存根或编写CompanyRepository的实现,并将其注入控制器(您可能需要为CompanyRepository添加Setter方法) 对于功能或集成测试,我将使用以下方法
@ContextConfiguration("file:my-context-file.xml")
@RunWith(SpringJUnit4ClassRunner.class)
在上下文文件中,我希望您配置测试运行所需的bean。我也遇到了同样的问题。我建议您不要使用
MongoRepository
访问MongoDB,原因如下:
- 它用于简单的查询,如findByxxx或findonebyxxandyyy。如果您想要更复杂的查询,即使@query也无法提供您想要的
- 由于所有查询条件都是由接口定义的,所以很容易出现语法错误
- 无法定义动态查询,例如根据上下文、字段、聚合等进行搜索
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd">
<bean name="fongo" class="com.github.fakemongo.Fongo">
<constructor-arg value="InMemoryMongo" />
</bean>
<bean id="mongo" factory-bean="fongo" factory-method="getMongo" />
<mongo:db-factory id="mongoDbFactory" mongo-ref="mongo" />
<!-- localhost settings for mongo -->
<!--<mongo:db-factory id="mongoDbFactory" /> -->
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg ref="mongoDbFactory" />
</bean>
<!-- Base package to scan the mongo repositories -->
<!-- Set your CompanyRepository package -->
<mongo:repositories base-package="package.to.repositories" />
</beans>
关键是如何验证create方法是否会导致持久化新实例。当将关系数据库与ORM结合使用时,我只是将db层切换到内存,并对测试db实例进行断言。在mongo的情况下,我不能这样做,所以我考虑了模拟(就像你建议的那样)和测试save方法的执行。这种方式也不起作用,因为Mockito无法处理从MongoRepository(定义save方法)继承的CompanyRepository。当然,CompanyController的创建方法非常简单,但同样的问题存在于更复杂的操作中。我已经解决了。varify方法存在问题:verify(repositoryMock,times(1)).save(any(Iterable.class));应该有Company.class而不是Iterable.class-我没有注意到这一点,并且认为Mockito无法验证继承的方法。。。写一个基于hashmap的CompanyRepository就是我所做的,而且效果很好。由于所有存储库都有一个相似的接口,因此为所有存储库编写一个基于抽象hashmap的实现非常容易。然后,特定的存储库对抽象实现进行子类化。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd">
<bean name="fongo" class="com.github.fakemongo.Fongo">
<constructor-arg value="InMemoryMongo" />
</bean>
<bean id="mongo" factory-bean="fongo" factory-method="getMongo" />
<mongo:db-factory id="mongoDbFactory" mongo-ref="mongo" />
<!-- localhost settings for mongo -->
<!--<mongo:db-factory id="mongoDbFactory" /> -->
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg ref="mongoDbFactory" />
</bean>
<!-- Base package to scan the mongo repositories -->
<!-- Set your CompanyRepository package -->
<mongo:repositories base-package="package.to.repositories" />
</beans>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"/mongo-config.xml"})
public class CompanyTest {
@Autowired
private MongoOperations mongoOperations;
@Resource
private CompanyRepository companyRepository;
@Test
public void foo() {
// Define test logic
}
}