简体   繁体   English

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

[英]Unit testing with mongodb repositories

I have built application that uses MongoDB and I have came across problem with testing. 我已经构建了使用MongoDB的应用程序,我遇到了测试问题。

As long as I used JPA and some relational database I used some test layer which switched persistence to in-memory database (linke HSQLDB or MySQL) for test purposes. 只要我使用JPA和一些关系数据库,我就使用了一些测试层,它将持久性切换到内存数据库(linke HSQLDB或MySQL)进行测试。 This way I was able to limit IO operations and speed up tests. 这样我就可以限制IO操作并加快测试速度。 However with MongoDB and Spring Data it is very convenient to use repositories based on interfaces extending MongoRepository. 但是使用MongoDB和Spring Data,使用基于扩展MongoRepository的接口的存储库非常方便。

My question is how to deal with unit testing and functional testing when using repositories? 我的问题是如何在使用存储库时处理单元测试和功能测试? For example I have simple class that is anotated as mongo document: 例如,我有一个简单的类,可以作为mongo文档进行anotated:

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;
}    
}

and related repository: 和相关的存储库:

@Repository
public interface CompanyRepository extends MongoRepository<Company, Serializable> {
}

It is used in coresponding controller: 它用于相应的控制器:

@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);
}
}

Finally I made two tests (however it could be one covering both tasks) that covers controller api and data format (where I mock repository to prevent from IO operations, and it works nice), and second where I want to make sure that passed object was persisted successfuly. 最后,我做了两个测试(但它可能是一个涵盖两个任务),涵盖了控制器api和数据格式(我模拟存储库以防止IO操作,它工作得很好),第二个我想确保传递的对象坚持不懈地坚持下去。 As it cames out it is not so simple. 因为它出来并不是那么简单。 First of all (correct me if I'm wrong) there is no in-memory mongo implementation. 首先(如果我错了,请纠正我)没有内存中的mongo实现。 Secondly, I cannot verify with mockito execution of save method because of inheritance I suppose. 其次,由于我认为继承,我无法用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));
}
}

I thought about introducing a dao layer between controller and repository that could be mocked and verified bot it adds more complexity and force to encapsulate each method used by repository. 我想在控制器和存储库之间引入一个可以模拟和验证的dao层,它会增加更多的复杂性并强制封装存储库使用的每个方法。 Also it doesn't fix problem but partially moves it to lower level. 它也没有解决问题,但部分将其移至较低级别。 Is there any method or practice that could help to deal with this kind of problem? 是否有任何方法或实践可以帮助解决这类问题? Or maybe I should use different approach with mongo? 或者也许我应该使用mongo的不同方法?

For unit testing I would stub or write an implementation of CompanyRepository and inject that into your controller (you may need to add a Setter method for CompanyRepository). 对于单元测试,我会存根或编写CompanyRepository的实现并将其注入您的控制器(您可能需要为CompanyRepository添加Setter方法)。

For Functional or integration testing I would look at using the following 对于功能或集成测试,我将使用以下内容

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

In the context file I would expect you to configure the beans that are only required for your test to run. 在上下文文件中,我希望您配置仅运行测试所需的bean。

I have faced the same problem. 我遇到了同样的问题。 I recommend you NOT to use MongoRepository to access to MongoDB for several reasons: 我建议您不要使用MongoRepository来访问MongoDB,原因如下:

  • It is used for easy queries, like findByxxx or findOneByxxxAndyyy. 它用于简单查询,例如findByxxx或findOneByxxxAndyyy。 If you want more complex query, even @Query cannot give you what you want. 如果你想要更复杂的查询,即使@Query也无法满足你的需求。
  • It is easy to make syntax mistake, because all query conditions are defined by interface. 很容易使语法错误,因为所有查询条件都是由接口定义的。
  • Cannot define dynamic queries, like search depending on context, fields, aggregations, etc... 无法定义动态查询,例如根据上下文,字段,聚合等进行搜索...

Instead, I recommend you to use MongoOperations . 相反,我建议你使用MongoOperations It accepts dynamic query with simple/complex criteria, very easy syntax to write your query, etc... For mocking, use Fongo framework, so it is 100% in memory database, so you can do all CRUD operations for asserts... 它接受具有简单/复杂标准的动态查询,非常容易编写查询的语法等等...对于模拟,使用Fongo框架,因此它在内存数据库中是100%,因此您可以对断言执行所有CRUD操作...

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

How to mock https://github.com/fakemongo/fongo#usage-details 如何模拟https://github.com/fakemongo/fongo#usage-details

UPDATE: If you still need to mock MongoDB repository, use this xml definition: 更新:如果您仍需要模拟MongoDB存储库,请使用此xml定义:

mongo-config.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>

Define your test class by this way: 通过这种方式定义您的测试类:

@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
    }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM