後臺單元測試參數模擬使用Mockito.any(.class)而非Mockito.any()

前言

本週開始就正式開始上課了,雖然說這學期開頭不怎麼好,可是這學期的課都還挺不錯的,學着頗有意思,在團隊學習了這麼長時間,再加上半年多沒在教室學習,致使我對老師講課的方式不是很適應,「萬課皆理論」這種方式在我看來是不會教,可是在和團隊的同窗討論以後,發現是我片面了,畢竟對於大多數人來講,學習的目的是拿高成績,高績點,而我想的是要學會怎麼用,反正無論怎麼樣吧,都會好好學的。java

問題

@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<Subject> subjects = new ArrayList<>();
        subjects.add(subject);
        Page<Subject> 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()方法進行的單元測試,而後看一下報錯:git

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

解決

還記得上週凱強在彙報中提到了沒有JSON字符Value的問題,而後打印一下JSON字符串看一下具體問題:
image.png
在迴應中Body屬性爲空,什麼都沒返回,而後在凱強的指導下把參數傳輸所有改成Mockito.any(),而後:
image.pnggithub

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:json

@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<Subject> subjects = new ArrayList<>();
        subjects.add(subject);
        Page<Subject> 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(<T>.class)就要嚴格對應,就能發現問題,至於爲啥會出現這種問題還沒搞明白,多是SpringBoot測試的一種機制吧。segmentfault

緣由

在通過潘老師講解,因爲接收的是collegeId,那麼courseId接受的值爲null,null不是Long類型,沒法匹配,就不認爲是原來的page方法,可是換爲any()就能正常匹配,而後就能經過測試了。dom

結語

隨着對單元測試接觸的愈來愈多,暴露的問題也愈來愈多,注意的要點也愈來愈多,發現單元測試也沒有以前想的那麼難了。
千淘萬漉雖辛苦,吹盡狂沙始到金。單元測試

本文做者:河北工業大學夢雲智開發團隊 張文達學習

相關文章
相關標籤/搜索