单元测试

控制层测试

这一层是针对接口的测试

使用技术

  1. MockMVC

    https://www.jianshu.com/p/91045b0415f0

  2. Mokcito

    https://github.com/hehonghui/mockito-doc-zh

示例

测试方法com.xh.cloudworkstatistic.application.StudentWorkAppService#getStuWorkWritingContent
@ActiveProfiles("local")
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
public class StudentWorkCtrlTest {

    //将@Mock注解的对象注入到studentWorkCtrl下
    @InjectMocks
    private StudentWorkCtrl studentWorkCtrl;

    @Mock
    private StudentWorkAppService studentWorkAppService;

    private MockMvc mockMvc;

    @Before
    public void setUp(){
        MockitoAnnotations.initMocks(this);
        mockMvc = MockMvcBuilders.standaloneSetup(studentWorkCtrl).build();
    }

    /**
     * 批量获取学生作业的学生答题笔记
     *
     * schoolId 学校ID(默认为0查全部)
     * size 获取条数
     * subject 学科(默认为0查全部)
     * lastTime 上次获取的最小createTime(毫秒)
     */
    @Test
    public void findWorkList() throws Exception {
        //准备数据
        long schoolId = 0L;
        int size = 10;
        int subject = 0;
        long lastTime = 1511718859000L;
        List<TopicHandWritingContent> contents = Lists.newArrayListWithExpectedSize(1);
        TopicHandWritingContent topicHandWritingContent = new TopicHandWritingContent();
        topicHandWritingContent.setTopicId("10086");
        contents.add(topicHandWritingContent);
        
        //mock,打桩
when(studentWorkAppService.getStuWorkWritingContent(schoolId,size,subject,lastTime)).thenReturn(contents);
        
        //调用
 mockMvc.perform(MockMvcRequestBuilders.get("/intranet/studentWorks/schools/"+schoolId+"/")
                .accept(MediaType.APPLICATION_JSON_UTF8_VALUE)
                .param("size",size+"")
                .param("subject",subject+"")
                .param("lastTime",lastTime+""))
                //打印调用结果
                .andDo(MockMvcResultHandlers.print())
                //校验
                .andExpect(MockMvcResultMatchers.content().string("[{\"topicId\":\"10086\",\"handWritingContents\":[]}]"))
                .andReturn();
    }
}

业务逻辑层测试

使用技术

Mokcito

示例

public class StudentWorkServiceTest {
    @InjectMocks
    private StudentWorkAppService studentWorkAppService;

    @Mock
    private StudentWorkRepository studentWorkRepository;

    @Mock
    private FileManager fileManager;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
    }

    /**
     * 批量获取学生作业的学生答题笔记
     * <p>
     * schoolId 学校ID(默认为0查全部)
     * size 获取条数
     * subject 学科(默认为0查全部)
     * lastTime 上次获取的最小createTime(毫秒)
     */
    @Test
    public void findWorkList() throws Exception {
        //准备数据
        long schoolId = 0L;
        int size = 10;
        int subject = 0;
        long lastTime = 1511718859000L;

        List<StudentWork> studentWorks = Lists.newArrayListWithCapacity(1);
        StudentWork studentWork1 = new StudentWork();
        studentWork1.setWorkVersion(1);
        studentWork1.setSubmitContentUrl("a");
        Set<Answer> answers = Sets.newHashSet();
        Answer answer = new Answer();
        answer.setTopicTraceUrl("http://www.baidu.com/");
        answer.setTopicId("t1");
        answers.add(answer);
        studentWork1.setAnswers(answers);
        studentWorks.add(studentWork1);

        StudentWork studentWork2 = new StudentWork();
        studentWork2.setWorkVersion(0);
        studentWork2.setSubmitContentUrl("b");
        studentWork2.setAnswers(answers);
        studentWorks.add(studentWork2);

        Map<String, List<HandWritingContent>> stuHandWritingMap = Maps.newHashMap();
        List<HandWritingContent> handWritingContents = Lists.newArrayListWithCapacity(1);
        HandWritingContent handWritingContent = new HandWritingContent(1, "abc");
        handWritingContents.add(handWritingContent);
        stuHandWritingMap.put("topicId", handWritingContents);

        //打桩
 when(studentWorkRepository.findAllFields(schoolId, size, subject, lastTime)).thenReturn(studentWorks);
        when(fileManager.analysisSubmitContentUrl(studentWorks.get(1).getSubmitContentUrl())).thenReturn(stuHandWritingMap);
        when(fileManager.analysisSingleTopicUrl(studentWorks.get(0).getAnswers().iterator().next().getTopicTraceUrl())).thenReturn(handWritingContents);

        //调用
        List<TopicHandWritingContent> workWritingContent = studentWorkAppService.getStuWorkWritingContent(schoolId, size, subject, lastTime);

        //断言
        Assert.assertEquals(workWritingContent.get(0).getTopicId(), "t1");
        Assert.assertEquals(workWritingContent.get(1).getTopicId(), "topicId");
        //校验方法的运行次数
        verify(fileManager, times(1)).analysisSubmitContentUrl(studentWorks.get(1).getSubmitContentUrl());
        verify(fileManager, times(1)).analysisSingleTopicUrl(studentWorks.get(0).getAnswers().iterator().next().getTopicTraceUrl());
    }
}

持久层测试

数据库选用

Fongo

​ Fongo是MongoDB的内存Java实现。它拦截对标准mongo-java-driver的调用,以查找,更新,插入,移除和其他方法。主要用途是用于不想启动mongod进程的轻量级单元测试。

使用
Fongo fongo = new Fongo("mongo server 1");
DB db = fongo.getDB("mydb");
DBCollection collection = db.getCollection("mycollection");
collection.insert(new BasicDBObject("name", "jon"));
整合SpringBoot

不能使用url进行连接数据库

@Configuration
public class MongoTestConfiguration {

    @Bean
    public Fongo fongo(){
        return new Fongo("InMemoryMongo");
    }

    @Bean
    public MongoClient mongo(){
        return fongo().getMongo();
    }

    @Bean
    public MongoTemplate mongoTemplate(){
        return new MongoTemplate(mongo(),"test");
    }
    
}

由于使用Fongo不能和原项目中的Mongo灵活混用,所以放弃这种数据库,因为一个是对象创建的MongoTemplate,一个是uri创建的MongoTemplate,在使用过程中还需要切换运行的代码。

Embed Mongo

为在单元测试中运行mongodb提供一种平台中立的方式,本质上是运行之前下载一个mongodb数据库,下载的数据库版本可以进行设置,使用之后数据清除,运行过程中可以使用可视化工具进行连接

使用
spring:
  data:
    mongodb:
      uri: mongodb://localhost:12345/xh_cloudwork_test
      exam:
        uri: mongodb://localhost:12345/xh_cloudwork_exam
@Configuration
public class MongoTestConfiguration {

    @Value("${spring.data.mongodb.uri}")
    private String uri;

    @Value("${spring.data.mongodb.exam.uri}")
    private String examUri;

    @Bean(name = "mongoTemplate")
    public MongoTemplate getDefaultMongoTemplate() throws Exception {
        return new MongoTemplate(mongoDbFactory(uri));
    }

    @Bean(name = "examMongoTemplate")
    public MongoTemplate examMongoTemplate() throws Exception {
        return new MongoTemplate(mongoDbFactory(examUri));
    }

    private MongoDbFactory mongoDbFactory(String uri) throws Exception {
        return new SimpleMongoDbFactory(new MongoClientURI(uri));
    }
}
@ActiveProfiles("unit")
@RunWith(SpringRunner.class)
@SpringBootTest(classes={TestApp.class})
public class TestDemo {


    @Autowired
    MongoTemplate mongoTemplate;

    @Autowired
    MongoTemplate examMongoTemplate;

    MongodProcess mongod = null;

    @Before
    public void setUp() throws IOException {
        MongodStarter starter = MongodStarter.getDefaultInstance();
        String bindIp = "localhost";
        int port = 12345;
        IMongodConfig mongodConfig = new MongodConfigBuilder()
                .version(Version.Main.PRODUCTION)
                .net(new Net(bindIp, port, Network.localhostIsIPv6()))
                .timeout(new Timeout(1000))
                .build();

        MongodExecutable mongodExecutable = null;
        try {
            mongodExecutable = starter.prepare(mongodConfig);
            mongod = mongodExecutable.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void test(){
        Work work = new Work();
        work.setHandIn(3);
        work.setSchoolId(20);
        work.setHandInEmendNum(5);
        work.setState(1);
        examMongoTemplate.insert(work);
        mongoTemplate.insert(work);
        Work work1 = workRepository.findById(work.getWorkId());
        System.out.println(work1.getSchoolId());
    }

    @After
    public void close(){
        mongod.stop();
    }
}
image-20200116111640179.png

如上例,只需要切换ActiveProfiles就可以灵活的切换数据库进行测试

示例

@ActiveProfiles("unit")
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
public class StudentWorkDaoTest {

    @Autowired
    MongoTemplate mongoTemplate;

    @Autowired
    MongoTemplate examMongoTemplate;

    @Autowired
    WorkManager workManager;


    MongodProcess mongod = null;

    @Before
    public void startMongo() throws IOException {
        MongodStarter starter = MongodStarter.getDefaultInstance();
        String bindIp = "localhost";
        int port = 12345;
        IMongodConfig mongodConfig = new MongodConfigBuilder()
                .version(Version.Main.PRODUCTION)
                .net(new Net(bindIp, port, Network.localhostIsIPv6()))
                .timeout(new Timeout(1000))
                .build();

        MongodExecutable mongodExecutable = null;
        try {
            mongodExecutable = starter.prepare(mongodConfig);
            mongod = mongodExecutable.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void findAll(){
        //根据下列这些字段进行查询
        //long schoolId, long classId, long teacherId, int subject,
        //List<Integer> periods, String bookId, int size, long lastTime, long startTime, long endTime
        //准备数据
        setData();
        List<Integer> periods = Lists.newArrayList();
        periods.add(0);
        periods.add(1);
        List<Work> works = workManager.findAll(1, 123456, 36471,
                2, periods, "book01", 12,
                0L, 10L,
                22345678930L);
        Assert.assertEquals(works.size(),3);

    }

    private void setData(){
        for (int i = 1; i < 13; i++) {
            Work work = new Work();
            Group group = new Group();
            group.setGroupId(123456);
            List<Group> groups = Lists.newArrayList();
            groups.add(group);
            work.setTargets(groups);
            work.setSubject(i%4+1);
            work.setPeriod(i%4);
            work.setTeacherId(36471);
            work.setTeacherName("张淑虹");
            work.setState(1);
            work.setHandInEmendNum(123);
            work.setHandIn(4);
            work.setSchoolId(i%2);
            work.setCorrectNum(2);
            Title title = new Title("5b6166b0adf6460542bda0ca","单测用例",36471);
            List<Title> titles = Lists.newArrayList();
            titles.add(title);
            work.setTitles(titles);
            Topic topic = new Topic();
            topic.setTopicId("topic01");
            topic.setScore(80);
            topic.setType(i%3);
            topic.setBookId("book01");
            List<Topic> topics = Lists.newArrayList();
            topics.add(topic);
            work.setTopics(topics);
            work.setCreateTime(12345678910L+i);
            mongoTemplate.insert(work);
        }
    }

    @After
    public void close(){
        mongod.stop();
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342