使用mongodb存储库进行单元测试

时间:2014-02-03 07:55:14

标签: java spring mongodb unit-testing spring-data-mongodb

我已经构建了使用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的不同方法?

2 个答案:

答案 0 :(得分:1)

对于单元测试,我会存根或编写CompanyRepository的实现并将其注入您的控制器(您可能需要为CompanyRepository添加Setter方法)。

对于功能测试或集成测试,我将使用以下

@ContextConfiguration("file:my-context-file.xml")
@RunWith(SpringJUnit4ClassRunner.class)

在上下文文件中,我希望您配置仅运行测试所需的bean。

答案 1 :(得分:0)

我遇到了同样的问题。我建议您 NOT 使用MongoRepository访问MongoDB,原因如下:

  • 它用于简单查询,例如findByxxx或findOneByxxxAndyyy。如果您想要更复杂的查询,即使 @Query 也无法满足您的需求。
  • 很容易使语法错误,因为所有查询条件都是由接口定义的。
  • 无法定义动态查询,例如根据上下文,字段,聚合等进行搜索...

相反,我建议您使用 MongoOperations 。它接受具有简单/复杂标准的动态查询,非常容易编写查询的语法等等...对于模拟,使用Fongo框架,因此它在内存数据库中是100%,因此您可以对断言执行所有CRUD操作...

更多信息:http://docs.spring.io/spring-data/data-mongo/docs/current/api/org/springframework/data/mongodb/core/MongoOperations.html

如何模拟 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
    }

}