后台单元测试参数模拟使用Mockito.any(.class)而非Mockito.any()

前言

本周开始就正式开始上课了,虽说这学期开头不怎么好,但是这学期的课都还挺不错的,学着很有意思,在团队学习了这么长时间,再加上半年多没在教室学习,导致我对老师讲课的方式不是很适应,“万课皆理论”这种方式在我看来是不会教,但是在和团队的同学讨论之后,发现是我片面了,毕竟对于大多数人来说,学习的目的是拿高成绩,高绩点,而我想的是要学会怎么用,反正不管怎么样吧,都会好好学的。

问题

  @Test
    void page() throws Exception {
        int page = new Random().nextInt();
        Long courseId = new Random().nextLong();
        Long modelId = new Random().nextLong();
        Integer difficult = new Random().nextInt();
        Subject subject = getOneSubject();
        Subject subSubject = getOneSubject();
        subject.setSubjects(Arrays.asList(subSubject));
        Tag tag = TagControllerTest.getOneTag();
        subject.setTags(Arrays.asList(tag));
        List subjects = new ArrayList<>();
        subjects.add(subject);
        Page subjectPage = new PageImpl(subjects);
        Mockito.doReturn(subjectPage).when(this.subjectService).page(Mockito.any(), Mockito.any(Long.class), Mockito.any(Long.class), Mockito.any(Integer.class), Mockito.any());

        String url = baseUrl;
        this.mockMvc.perform(MockMvcRequestBuilders.get(url)
                .param("page", String.valueOf(page))
                .param("collegeId", courseId.toString())
                .param("modelId", modelId.toString())
                .param("difficult", difficult.toString())
                .param("tags", Arrays.asList(tag).toString()))
                .andDo(MockMvcResultHandlers.print())
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.content[0].id").exists())
                .andExpect(MockMvcResultMatchers.jsonPath("$.content[0].mark").exists())
                .andExpect(MockMvcResultMatchers.jsonPath("$.content[0].stem").exists())
                .andExpect(MockMvcResultMatchers.jsonPath("$.content[0].analysis").exists())
                .andExpect(MockMvcResultMatchers.jsonPath("$.content[0].createTime").exists())
                .andExpect(MockMvcResultMatchers.jsonPath("$.content[0].difficult").exists())
                .andExpect(MockMvcResultMatchers.jsonPath("$.content[0].used").exists())
                .andExpect(MockMvcResultMatchers.jsonPath("$.content[0].course").exists())
                .andExpect(MockMvcResultMatchers.jsonPath("$.content[0].course.name").exists())
                .andExpect(MockMvcResultMatchers.jsonPath("$.content[0].model").exists())
                .andExpect(MockMvcResultMatchers.jsonPath("$.content[0].options.length()").value(1))
                .andExpect(MockMvcResultMatchers.jsonPath("$.content[0].options[0].id").exists())
                .andExpect(MockMvcResultMatchers.jsonPath("$.content[0].options[0].content").exists())
                .andExpect(MockMvcResultMatchers.jsonPath("$.content[0].subjects.length()").value(1))
                .andExpect(MockMvcResultMatchers.jsonPath("$.content[0].subjects[0].id").exists())
                .andExpect(MockMvcResultMatchers.jsonPath("$.content[0].tags.length()").value(1))
                .andExpect(MockMvcResultMatchers.jsonPath("$.content[0].tags[0].id").exists())
                .andExpect(MockMvcResultMatchers.jsonPath("$.content[0].tags[0].name").exists());
    }

以上是对page()方法进行的单元测试,然后看一下报错:

java.lang.AssertionError: No value at JSON path "$.content[0].id"

解决

还记得上周凯强在汇报中提到了没有JSON字符Value的问题,然后打印一下JSON字符串看一下具体问题:
后台单元测试参数模拟使用Mockito.any(<T>.class)而非Mockito.any()_第1张图片

Mockito.doReturn(subjectPage).when(this.subjectService).page(Mockito.any(), Mockito.any(),Mockito.any(), Mockito.any(), Mockito.any());

经过排查发现是参数名定义错了,courseId定义成了collegeId,但是由此引发疑问,为何定义Mockito.any()就不报错,而定义Mockito.any(Long.class)就会出现问题呢,我们再对另一个变量进行测试,将modelId 传为 model:

    @Test
    void page() throws Exception {
        int page = new Random().nextInt();
        Long courseId = new Random().nextLong();
        Long modelId = new Random().nextLong();
        Integer difficult = new Random().nextInt();
        Subject subject = getOneSubject();
        Subject subSubject = getOneSubject();
        subject.setSubjects(Arrays.asList(subSubject));
        Tag tag = TagControllerTest.getOneTag();
        subject.setTags(Arrays.asList(tag));
        List subjects = new ArrayList<>();
        subjects.add(subject);
        Page subjectPage = new PageImpl(subjects);

        Mockito.doReturn(subjectPage).when(this.subjectService).page(Mockito.any(), Mockito.any(Long.class), Mockito.any(Long.class), Mockito.any(Integer.class), Mockito.any());

        String url = baseUrl;
        this.mockMvc.perform(MockMvcRequestBuilders.get(url)
                .param("page", String.valueOf(page))
                .param("courseId", courseId.toString())
                .param("model", modelId.toString())
                .param("difficult", difficult.toString())
                .param("tags", Arrays.asList(tag).toString()))
                .andDo(MockMvcResultHandlers.print())
                .andExpect(MockMvcResultMatchers.status().isOk());
    }

然后就会出现同样的错误,Response里面的Body属性没有值,但是如果更改为Mockito.any()就能通过测试了。
所以说之前的问题并非个例,而是普遍存在的,如果使用Mockito.any()就会导致测试不严谨,字段不对应也不能知道,而使用Mockito.any(.class)就要严格对应,就能发现问题,至于为啥会出现这种问题还没搞明白,可能是SpringBoot测试的一种机制吧。

原因

在经过潘老师讲解,由于接收的是collegeId,那么courseId接受的值为null,null不是Long类型,无法匹配,就不认为是原来的page方法,但是换为any()就能正常匹配,然后就能通过测试了。

结语

随着对单元测试接触的越来越多,暴露的问题也越来越多,注意的要点也越来越多,发现单元测试也没有之前想的那么难了。
千淘万漉虽辛苦,吹尽狂沙始到金。

本文作者:河北工业大学梦云智开发团队 张文达

你可能感兴趣的:(bootstrap,单元测试)