JAVA单元测试—JUNIT+Mockito

一、引入依赖:

当需要对静态类进行mock的时候:mockito-core的版本要在3.4以上,还要引入mockito-core依赖的其他jar,要引入mockito-inline,junit包要放在mockito-inline之前,引入的时候如果报错,请检查maven的setting文件,配置的仓库路径是否能正常下载、

  
        
        
            org.springframework.boot
            spring-boot-starter-web
            3.0.4
        
        
        
            org.springframework.boot
            spring-boot-starter-test
            2.3.9.RELEASE
            
                
                    org.mockito
                    mockito-junit-jupiter
                
                
                    org.mockito
                    mockito-core
                
            
        
        
            org.mockito
            mockito-junit-jupiter
            4.5.1
            test
        
        
            org.junit.jupiter
            junit-jupiter-api
            test
        
        
            junit
            junit
            test
        
        
            org.mockito
            mockito-core
            3.9.0
            test
        
        
            org.mockito
            mockito-inline
            4.5.1
            test
        
        
            net.bytebuddy
            byte-buddy
            1.10.22
            test
        
        
            net.bytebuddy
            byte-buddy-agent
            1.10.22
            test
        
    

二、Test测试的配置:

1、检查@ComponentScan 是否正常扫描到了路径

2、设置idea-file-project structure-moudles-选中模块-Sources-将test下面的java文件夹路径设置为tests(选中java点击tests)

JAVA单元测试—JUNIT+Mockito_第1张图片

3、选中要测试的service,右键-generate-选择test-

JAVA单元测试—JUNIT+Mockito_第2张图片

 选择Junit的版本、测试名类称、路径(最好不要改了,与service的位置一致),选择要测试的方法JAVA单元测试—JUNIT+Mockito_第3张图片

 4、测试覆盖率配置

当写完测试类debug一下之后,edit Config....

JAVA单元测试—JUNIT+Mockito_第4张图片

选中测试类然后选class、选中下面红色的第一个,然后点-删去,点+添加进第二个,然后点击上图框住的小按钮,就可以查覆盖率了

 JAVA单元测试—JUNIT+Mockito_第5张图片

 JAVA单元测试—JUNIT+Mockito_第6张图片

三、注解的简单应用和语法:

1、注释测试类

  1. @SpringBootTest()

  2. @RunWith(SpringRunner.class)

2、注释成员变量

方式一:

@InjectMock|@Mock:

@InjectMock:注释的是要测试的实现类,不是接口 

@Mock:注释测试类serviceimpl用@Autowired引入的成员变量(mock后真实的方法不再调用)

但用Mockito.when(service.方法名(参数)).thenCallRealMethod();还是可以调真实的方法

@Spy:注释测试类serviceimpl用@Autowired引入的成员变量(spy真实的调用不调用要看写法)

但用Mockito.doReturn("不执行此方法").when(service).方法名(参数);还是可以不调用真实的方法

@Spy    会真实的执行对象的方法(如果方法报错,test直接报错)

@Mock 不会执行对象的方法(即使方法报错,test只会使用你设置的返回值,不影响流程)

方式二:

@Autowired| ReflectionTestUtils.setField();

@Autowired注释被注入mock的被测试的类

AopTestUtils.getTargetObject()、AopTestUtils.getUltimateTargetObject()

ReflectionTestUtils.setField();将其他的mock对象注入被测试的类中

@Autowired和@InjectMock最好不要一起使用,如果一起使用了

a、必须在test之前@Before中执行 MockitoAnnotations.openMocks(this);对mock初始化

b、测试的时候要用this.serviceimpl.方法名()测试  否则mock还会走真实的方法

3、手动的mock

需要手动再注入一下,不建议使用

ReflectionTestUtils.setField();

Student student=Mockito.mock(Student.class);

Student student=Mockito.spy(Student.class);

4、参数:Mockito.any()(不校验参数类型)、Mockito.anyString()、Mockito.eq()等等

当方法实际传入的参数如果是null,则设置的mock的返回值会不生效,所以传入的参数Mockito.when(service.方法名(Mockito.anyString())).thenReturn(要返回的值);

service.方法名(null);   //name上面的返回值失效了

5、方法

Mockito.when(service.方法名(参数)).thenReturn(要返回的值);
Mockito.when(service.方法名(参数)).thenCallRealMethod();
Mockito.when(service.方法名(参数)).thenThrow(new RuntimeException());
Mockito.doNothing().when(service).方法名(参数);
Mockito.doReturn("不执行此方法").when(service).方法名(参数);
new 对象的:MockedConstruction
静态方法:MockedStatic

6、Assert结果断言、verify行为验证

三、单元测试案例:

1、mock   通过@Autowired注入的对象的方法(有返回值、无返回值)

2、mock   通过new的对象的方法

3、mock   调用的静态方法(有返回值、无返回值)

4、mock   同一个类的静态方法、私有方法、非私有方法

5、通过反射的方式测试私有方法

四、代码示例

public class Util {
    public  static  String    getNum(){
         int i=1/0;
         return "123";
    }
    public  static  void    skipNum(){
         int i=1/0;
    }
}
public class Sertn {
    public String  setInedx(String shh ){
       int i=1/0;
       return "haha";
    }
    public void  setInedxVoid(String shh ){
        int i=1/0;
    }
    private String  getIndex(){
        int i=1/0;
        return "private";
    }
}
package com.springcloud.my.stock.service.impl;

import com.springcloud.my.stock.bean.Student;
import com.springcloud.my.stock.service.MyClassService;
import com.springcloud.my.stock.service.MyTestService;
import com.springcloud.my.stock.service.StudentService;
import com.springcloud.my.stock.util.Sertn;
import com.springcloud.my.stock.util.Util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class MyClassServiceImpl implements MyClassService {

    @Autowired
    private  StudentService studentService;

    @Override
    public String doHomeWork(String name) {
       //有返回值
       String result= studentService.doHomeWork(name);
        Student student=new Student();
        student.setName(name);
        String result2=  studentService.doHomeWork2(name,student);
       //无返回值
       studentService.doMathHomeWork();
       return result+"MyClassService"+result2;
    }

    /***静态方法 和new方法**/
    @Override
    public String getNumber() {
        studentService.doMathHomeWork();
        Util.skipNum();
        String num=Util.getNum();
        Sertn sertn=new Sertn();
        String hh=sertn.setInedx("");
        sertn.setInedxVoid();
        return num+hh;
    }
    /***自己类里面的方法**/
    @Override
    public String getOwnNumber() {
        studentService.doMathHomeWork();
        String gethahaha=getHaHa();
        String gethehehe=getHeHe();
        getTranslation();
        return gethahaha+gethehehe;
    }
    public void getTranslation(){
        int i=1/0;
        System.out.println("MyClassService getTranslation");
    }
    public String getHaHa(){
        int i=1/0;
        return "MyClassService haha";
    }
    private String getHeHe(){
        int i=1/0;
        return "hehe";
    }
}
package com.springcloud.my.stock.service;

import com.springcloud.my.stock.bean.Student;
import com.springcloud.my.stock.service.impl.MyClassServiceImpl;
import com.springcloud.my.stock.util.Sertn;
import com.springcloud.my.stock.util.Util;
import lombok.SneakyThrows;
import org.junit.Assert;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.*;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@SpringBootTest()
@RunWith(SpringRunner.class)
public class MyClassServiceTest {
    @InjectMocks
    //spy标签是因为要对MyClassServiceImpl 非测试的其他的方法进行mock
    @Spy
    private MyClassServiceImpl myClassService;

    /**如果是@Mock那么doReturn和thenReturn都不会执行真正的方法,
     * 如果是@Spy 那么doReturn不会执行真正的方法,thenReturn会执行真正的方法**/
    /**
     * 在这里Mock的成员变量如果不在下面设置返回值,返回的结果是返回类型的空值
     */
    @Mock
    private StudentService studentService;

    private AutoCloseable closeable;

    @Spy
    private MyClassServiceImpl myClassServiceSpy;
    @Before
    public void init() {
         closeable= MockitoAnnotations.openMocks(this);
        /***
         * MyClassServiceImpl  上的注解如果是@Autowired  而不是@InjectMocks  需要用下面的代码手动的注入
         *   MyClassServiceImpl myClassService1= AopTestUtils.getTargetObject(myClassService);
         *         ReflectionTestUtils.setField(myClassService1, "studentService",studentService);
         */
    }
    @After
    @SneakyThrows
    public void afterProject() {
        closeable.close();
    }
    @Test
    public void doHomeWorkTest() {
        //正常的测试 String result=myClassService.doHomeWork("张三");
        // 返回值 StudentService张三MyClassService张三

        //mock后
        String result=myClassService.doHomeWork("张三");
        Assert.assertEquals(result,"nullMyClassServicenull");

        /**无返回值的方法   可以直接跳过**/
        Mockito.doNothing().when(studentService).doMathHomeWork();
        /**如果studentService.doHomeWork()的参数是123 返回设置的返回值且不执行该方法***/
        Mockito.doReturn("不执行此方法").when(studentService).doHomeWork("123");
        Mockito.doReturn("不执行此方法111").when(studentService).doHomeWork("456");
        result=myClassService.doHomeWork("张三");
        Assert.assertEquals(result,"nullMyClassServicenull");
        result=myClassService.doHomeWork("123");
        Assert.assertEquals(result,"不执行此方法MyClassServicenull");
        result=myClassService.doHomeWork("456");
        Assert.assertEquals(result,"不执行此方法111MyClassServicenull");
        /**when thenReturn的写法***/
        Mockito.when(studentService.doHomeWork("456")).thenReturn("可能执行");
        result=myClassService.doHomeWork("456");
        Assert.assertEquals(result,"可能执行MyClassServicenull");

        /***无论studentService.doHomeWork()的参数是什么,都返回设定的值且不执行该方法**/
        Mockito.doReturn("不执行此方法").when(studentService).doHomeWork(Mockito.anyString());
        result=myClassService.doHomeWork("张三");
        Assert.assertEquals(result,"不执行此方法MyClassServicenull");

        /***当方法有多个参数时,参数必须都是Mockito. 的,或者都不是Mockito. 的 ,具体值的参数要用Mockito.eq包起来***/
        /*****不论传入的*****/
        Mockito.doReturn("*****").when(studentService)
                .doHomeWork2(Mockito.eq("李四"),Mockito.any());
        Mockito.doReturn("XXXXX").when(studentService)
                .doHomeWork2(Mockito.eq("李五"),Mockito.any());
        result=myClassService.doHomeWork("张三");
        Assert.assertEquals(result,"不执行此方法MyClassServicenull");
        result=myClassService.doHomeWork("李四");
        Assert.assertEquals(result,"不执行此方法MyClassService*****");
        result=myClassService.doHomeWork("李五");
        Assert.assertEquals(result,"不执行此方法MyClassServiceXXXXX");

        /***如果参数是对象***/
        Mockito.when(studentService.doHomeWork2(Mockito.any(),Mockito.any())).thenAnswer(mock->{
            /**1表示取方法的第一个参数**/
            Student student=mock.getArgument(1);
            if(student.getName().equals("哈哈")){
                return "Student";
            }else{
                return "";
            }
        });
        result=myClassService.doHomeWork("哈哈");
        Assert.assertEquals(result,"不执行此方法MyClassServiceStudent");
        result=myClassService.doHomeWork("111");
        Assert.assertEquals(result,"不执行此方法MyClassService");

    }
    //test的包要引入 import org.junit.Test;
    @Test
    public void getNumberTest() {
        //静态方法
        //无返回值的静态方法不需要处理直接就会跳过
        MockedStatic utilMockedStatic=Mockito.mockStatic(Util.class);
        //new 对象的方法
        //无返回值的方法不需要处理直接就会跳过
        MockedConstruction sertnMockedConstruction=
                Mockito.mockConstruction(Sertn.class,((mock,context)->{
                    Mockito.doReturn("baba").when(mock).setInedx(Mockito.anyString());
                }));
        /**无返回值的方法   可以直接跳过**/
        Mockito.doNothing().when(studentService).doMathHomeWork();
        try{
            utilMockedStatic.when(()->Util.getNum()).thenReturn("000");
            String result=myClassService.getNumber();
            Assert.assertEquals(result,"000baba");
            //有返回值的静态方法
        }finally {
            //一定要关了。否则多个test会报错,
            //因为相同的类的MockedStatic、MockedConstruction一个线程只能有一个
            utilMockedStatic.close();
            sertnMockedConstruction.close();
        }
    }
    @Test
    public void getOwnNumberTest(){
        /***给myClassService加上 @Spy标签**/
        Mockito.doReturn("baba").when(myClassService).getHaHa();
        Mockito.doNothing().when(myClassService).getTranslation();
        String result=myClassService.getOwnNumber();
        //验证方法至少调用过一次
        verify(myClassService,atLeastOnce()).getHaHa();
        Assert.assertEquals(result,"000baba");
    }
    //测试私有方法
    @Test
    @SneakyThrows
    public void getHeHeTest(){
        //如果只是单纯的测试私有方法可以通过反射
        //如果想mock  私有方法,目前没有合适的方式  否则参考powermockito框架吧
        Method privateMethod = 
            MyClassServiceImpl.class.getDeclaredMethod("getHeHe", null);
        privateMethod.setAccessible(true);
        String result= (String) privateMethod.invoke(MyClassServiceImpl.class.newInstance(),null);
        //验证返回值是否一致
        Assert.assertEquals(result,"babahaha");
    }

}

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