JAVA单元测试—JUNIT+Mockito

一、引入依赖:

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

  <dependencies>
        <!-- 引入 SpringMVC 相关依赖,并实现对其的自动配置 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>3.0.4</version>
        </dependency>
        <!-- 引入 测试  集成的版本低去掉-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>2.3.9.RELEASE</version>
            <exclusions>
                <exclusion>
                    <groupId>org.mockito</groupId>
                    <artifactId>mockito-junit-jupiter</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.mockito</groupId>
                    <artifactId>mockito-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-junit-jupiter</artifactId>
            <version>4.5.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>3.9.0</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-inline</artifactId>
            <version>4.5.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>net.bytebuddy</groupId>
            <artifactId>byte-buddy</artifactId>
            <version>1.10.22</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>net.bytebuddy</groupId>
            <artifactId>byte-buddy-agent</artifactId>
            <version>1.10.22</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

二、Test测试的配置:

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

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

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

 选择Junit的版本、测试名类称、路径(最好不要改了,与service的位置一致),选择要测试的方法

 4、测试覆盖率配置

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

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

 

 

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

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<Util> utilMockedStatic=Mockito.mockStatic(Util.class);
        //new 对象的方法
        //无返回值的方法不需要处理直接就会跳过
        MockedConstruction<Sertn> 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");
    }

}

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring Boot是一个用于构建Java应用程序的开源框架,它提供了一种简化了配置的方式来快速构建应用程序。JUnit是一个用于编写和运行单元测试的开源测试框架,而Mockito是一个用于创建和管理模拟对象的Java库。 下面是一个使用Spring Boot、JUnitMockito进行单元测试的示例: 假设我们有一个UserService类,它依赖于一个UserRepository接口来访问数据库并进行一些操作。我们想要对UserService的方法进行单元测试。 首先,我们需要创建一个测试类,命名为UserServiceTest。在测试类中,我们将使用JUnit的注解来标记测试方法,并使用Mockito来创建模拟对象。示例代码如下: ```java @RunWith(MockitoJUnitRunner.class) public class UserServiceTest { @InjectMocks private UserService userService; @Mock private UserRepository userRepository; @Test public void testGetUserById() { // 配置模拟对象的行为 User user = new User("1", "John"); when(userRepository.findById("1")).thenReturn(user); // 调用被测试的方法 User result = userService.getUserById("1"); // 验证结果 assertEquals("John", result.getName()); } } ``` 在上面的示例中,我们使用了@RunWith注解来指定使用MockitoJUnitRunner运行测试,这样就能自动创建和管理模拟对象。使用@InjectMocks注解将被测试的对象自动注入到测试类中,使用@Mock注解创建模拟对象。 在testGetUserById方法中,我们首先使用when方法配置userRepository模拟对象的行为,表示当传入参数为"1"时,返回一个指定的User对象。 然后,我们通过调用userService的getUserById方法来测试该方法的逻辑。最后,使用assertEquals断言来验证结果是否符合预期。 以上就是一个使用Spring Boot、JUnitMockito进行单元测试的示例。通过使用Mockito创建模拟对象,我们可以更容易地测试各个方法的逻辑,而不依赖于实际的数据库。这样可以提高测试效率并确保代码的质量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值