1:前言

上一章说到了Redis测试工具,我们需要模拟的是Redis连接挡板。本章主要讲如何在单元测试中模拟Mongo服务器操作。

还是一如既往的不使用真正的服务器,直接用mock挡板。
本章要着重演示的是mongo里面有很的API,有的是方法里面new某个类,这个也要mock。除了@Mock注解外也可以用mock(类.class)方式。再着重演示入参是数组的方式写法。

2:Mongo模拟测试

请看下面实例:

import com.google.common.collect.Lists;
import com.mongodb.MongoClient;
import com.mongodb.client.ListDatabasesIterable;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.junit.Before;
import org.junit.Test;
import org.mongodb.morphia.Datastore;
import org.mongodb.morphia.Key;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Query;

import java.util.List;

import static org.mockito.Mockito.*;
import static org.assertj.core.api.Assertions.*;

/**
 * MongoDB工具测试
 *
 * @author: Cookie.Joo
 * @create: 2020/12/01
 */
public class MongoDBTest {

    private Datastore datastore;
    private MongoClient mongoClient;
    private MongoTemplate mongoTemplate;

    @Before
    public void setUp() {
        // Datastore mock
        datastore = mock(Datastore.class);
		//CreditTestEntity 是表实体类
        Key<CreditTestEntity> key = new Key<>(CreditTestEntity.class, "CreditTestEntity", new ObjectId());
        when(datastore.save(any(CreditTestEntity.class))).thenReturn(key);
        CreditTestEntity one = new CreditTestEntity();
        one.setId(new ObjectId());
        when(datastore.get(any(CreditTestEntity.class))).thenReturn(one);

        // MongoClient mock
        mongoClient = mock(MongoClient.class);
        ListDatabasesIterable<Document> documents = mock(ListDatabasesIterable.class);

        when(mongoClient.listDatabases()).thenReturn(documents);

        // MongoTemplate mock
        mongoTemplate = mock(MongoTemplate.class);

        CreditTestEntity entity = new CreditTestEntity();
        entity.setId(new ObjectId());
        entity.setAppId("com-app");
        List<CreditTestEntity> list = Lists.newArrayList(entity);
        when(mongoTemplate.find(new Query(), CreditTestEntity.class)).thenReturn(list);
        
        //gridFS里有个createFile方法特殊演示
    	GridFSInputFile gridFSInputFile = mock(GridFSInputFile.class);
        GridFS gridFS = mock(GridFS.class);
        //createFile方法是返回一个new GridFSInputFile的类,如果不mock的话会有空指针异常,我们借助PowerMockito来进行mock
        PowerMockito.whenNew(GridFS.class).withAnyArguments().thenReturn(gridFS);
        //createFile的入参是一个byte数组,数组的入参有些不一样,对于很多重载参数来说确实容易异常的地方,写法也很特殊。
        when(gridFS.createFile(any(byte[].class))).thenReturn(gridFSInputFile);
    }

    @Test
    public void saveTest() {
        CreditTestEntity entity = new CreditTestEntity();
        String id = datastore.save(entity).getId().toString();
        assertThat(id).as("校验datastore.save(entity)").isNotNull();
    }

    @Test
    public void getTest() {
        CreditTestEntity entity = datastore.get(new CreditTestEntity());
        assertThat(entity).as("校验datastore.get(entity)").isNotNull();
    }

    @Test
    public void listDatabasesTest() {
        ListDatabasesIterable<Document> documents = mongoClient.listDatabases();
        assertThat(documents).as("校验mongoClient.listDatabases()").isNotNull();
    }

    @Test
    public void findTest() {
        List<CreditTestEntity> list = mongoTemplate.find(new Query(), CreditTestEntity.class);
        assertThat(list).
                as("校验mongoTemplate.find()第一个元素appId的值是否是com-app").
                asList().
                hasSize(1).
                element(0).
                hasFieldOrPropertyWithValue("appId", "com-app");
    }
}

最后运行的结果

image-20201201145854005

3:总结

其实从第二章到本章第三章,其实都是千变一律的模拟一个第三方工具来测验的,可能会产生质疑,这会不会和实际上不一样。我个人认为并没有多大差别,连接任何第三方存储其实是连接工具的问题,我们并非测试连接工具,我们是优先测试我们的功能在某些特定的数据下是否能正常运行,if-else语句是否都覆盖整个流程,数据的准备落盘并非是我们的首要关系的。连接到第三方工具这一步骤我们要测试的并不频繁,连接顶多是调优第三方jar支持的参数调整,与关键业务流程没有太强烈关系。单元测试昨晚后,我们启动到我们的应用去再走一次完整流程,才是正确的测试方式。

本章分享结束。

上一篇 下一篇