我已经构建了使用MongoDB的应用程序,但我遇到了测试问题。
只要我使用JPA和一些关系数据库,我就会使用一些测试层将持久性切换到内存数据库(linke HSQLDB或MySQL)进行测试。这样我就可以限制IO操作并加快测试速度。 但是使用MongoDB和Spring Data,使用基于扩展MongoRepository的接口的存储库非常方便。
我的问题是如何在使用存储库时处理单元测试和功能测试?例如,我有一个简单的类,它被分配为mongo文档:
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> {
}
用于相应的控制器:
@RestController
public class CompanyController {
private static final Logger log = LoggerFactory.getLogger(CompanyController.class);
@Autowired
private CompanyRepository repository;
@RequestMapping(value = "/company", method = RequestMethod.POST)
public void create(@Valid @RequestBody(required = true) Company company) {
repository.save(company);
}
}
最后,我做了两个测试(但它可能是一个涵盖两个任务),涵盖了控制器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的不同方法?
答案 0 :(得分:1)
对于单元测试,我会存根或编写CompanyRepository的实现并将其注入您的控制器(您可能需要为CompanyRepository添加Setter方法)。
对于功能测试或集成测试,我将使用以下
@ContextConfiguration("file:my-context-file.xml")
@RunWith(SpringJUnit4ClassRunner.class)
在上下文文件中,我希望您配置仅运行测试所需的bean。
答案 1 :(得分:0)
我遇到了同样的问题。我建议您 NOT 使用MongoRepository
访问MongoDB,原因如下:
相反,我建议您使用 MongoOperations 。它接受具有简单/复杂标准的动态查询,非常容易编写查询的语法等等...对于模拟,使用Fongo框架,因此它在内存数据库中是100%,因此您可以对断言执行所有CRUD操作...
如何模拟 https://github.com/fakemongo/fongo#usage-details
<强>更新强> 如果您仍需要模拟MongoDB存储库,请使用此xml定义:
<强>蒙戈-config.xml中强>
<?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
}
}