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

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

我已经构建了使用MongoDB的应用程序,并且在测试中遇到了问题

只要我使用JPA和一些关系数据库,我就使用一些测试层,将持久性切换到内存数据库(linke HSQLDB或MySQL)进行测试。这样我就可以限制IO操作并加快测试速度。 然而,对于MongoDB和Spring数据,使用基于扩展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> {
}
最后,我做了两个测试(不过可能是一个同时包含两个任务的测试),涵盖了控制器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也无法提供您想要的
  • 由于所有查询条件都是由接口定义的,所以很容易出现语法错误
  • 无法定义动态查询,例如根据上下文、字段、聚合等进行搜索
相反,我建议您使用MongoOperations。它接受具有简单/复杂条件的动态查询、编写查询的非常简单的语法等。。。对于模拟,使用Fongo框架,所以它100%在内存数据库中,所以您可以对断言执行所有CRUD操作

更多信息:

如何嘲笑

更新: 如果仍然需要模拟MongoDB存储库,请使用以下xml定义:

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

关键是如何验证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
    }

}