java单元测试框架使用方法

被测试类

public class Example {
    public String doSomething() {
        System.out.println("doSomething");
        return "hello";
    }
}

Spy和Mock的区别

针对@Spy:

  • when(...) thenReturn(...)做了真实调用。只是返回了指定的结果
  • doReturn(...) when(...)不做真实调用

针对@Mock: 整个对象被mock

两者的区别:mock方法和spy方法都可以对对象进行mock。但是前者是接管了对象的全部方法,而后者只是将有桩实现(stubbing)的调用进行mock,其余方法仍然是实际调用。

怎么看使用的是哪个框架

下面说的用哪个测试框架,主要是看@Test是哪个框架的!!!

junit4

默认运行器(无需显式指定)



import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.BlockJUnit4ClassRunner;

//@RunWith(BlockJUnit4ClassRunner.class) // JUnit 4 默认使用 BlockJUnit4ClassRunner 来运行测试。这种情况下,你不需要显式添加 @RunWith 注解。
public class Example5Test {
    @Test
    public void testSomething() {
        System.out.println("This is a test");
    }
}

@RunWith 指定特殊运行器


当你需要增强测试功能或与外部框架集成时,可以使用 @RunWith 并指定运行器。

以下是常见基于 @RunWith 的第三方集成场景(在 JUnit 4 中):

  1.  Mockito: 使用 @RunWith(MockitoJUnitRunner.class),自动注解初始化。
  2.  Spring: 使用 @RunWith(SpringJUnit4ClassRunner.class) 来整合 Spring 框架。
  3. JUnit Parameterized: 使用 @RunWith(Parameterized.class) 执行参数化测试。
  4. Cucumber: 使用 @RunWith(Cucumber.class) 来运行 BDD 测试。
  5. 自定义 Runner: 用于添加自定义测试行为。

在 JUnit 5 中,@RunWith 被替换为 @ExtendWith,功能更加灵活且模块化。

@RunWith+mockito框架



import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;

import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.class)
public class Example6Test {
    @Spy
    private Example eSpy = new Example();

    @Test // 这个是org.junit.Test
    public void test1() {
        doReturn("hi").when(eSpy).doSomething();
        System.out.println(eSpy.doSomething());
        // 输出:
        // hi
    }

    @Test
    public void test2() {
        when(eSpy.doSomething()).thenReturn("ni hao");
        System.out.println(eSpy.doSomething());
        // 输出:
        // doSomething (来自doSomething方法的真实调用里打印的日志)
        // ni hao
    }
}

加载SpringBoot上下文



import org.example.springbootdemo.spring.SpringExample;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

// SpringRunner vs SpringJUnit4ClassRunner
//区别:
//SpringRunner 是 SpringJUnit4ClassRunner 的现代别名,它们的行为和功能几乎完全一致。
//推荐使用 SpringRunner.class,特别是在 Spring Boot 项目中,这是社区标准和趋势。

//适用场景:
//如果你是 Spring Boot 项目,优先选择 @RunWith(SpringRunner.class)。
//如果你是传统 Spring MVC 项目,仍然可以沿用 @RunWith(SpringJUnit4ClassRunner.class),也是完全有效的。
@RunWith(SpringRunner.class) // 或者 @RunWith(SpringJUnit4ClassRunner)
@SpringBootTest // 启动完整 SpringBoot 应用上下文
public class Spring1Test {
    @Autowired
    private SpringExample springExample;

    @Test
    public void test1() {
        springExample.voidFun();
        // 输出:
        // hello
    }
}

SpringBoot+@MockBean



import org.example.springbootdemo.spring.SpringExample;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;

import static org.mockito.Mockito.when;

@RunWith(SpringRunner.class) // 或者 @RunWith(SpringJUnit4ClassRunner)
@SpringBootTest // 启动完整 SpringBoot 应用上下文
public class Spring2Test {
    @MockBean // 替换 Spring 容器中的 springExample bean
    private SpringExample springExample;

    @Test
    public void test1() {
        springExample.voidFun();
        // 没有任何输出
    }

    @Test
    public void test2() {
        when(springExample.doSomething()).thenReturn("hi");
        System.out.println(springExample.doSomething());
        // 输出:
        // hi
    }
}

juint5+mockito

场景一:@ExtendWith+@Spy 或 Mockito.spy()



import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;

import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;


@ExtendWith(MockitoExtension.class) // JUnit 5 可以使用扩展 @ExtendWith(MockitoExtension.class) 自动将@Spy的对象初始化为spy对象
public class Example1Test {
    @Spy
    private Example eSpy = new Example();

    @Test
    public void test1() {
        doReturn("hi").when(eSpy).doSomething();
        System.out.println(eSpy.doSomething());
        // 输出:
        // hi
    }

    @Test
    public void test2() {
        when(eSpy.doSomething()).thenReturn("ni hao");
        System.out.println(eSpy.doSomething());
        // 输出:
        // doSomething (来自doSomething方法的真实调用里打印的日志)
        // ni hao
    }


    @Test
    public void test3() {
        // 这两句手动方式 等效于上面的 @Spy + @ExtendWith(MockitoExtension.class) 的方式
        Example example = new Example();
        Example exampleSpy = Mockito.spy(example);
        // -----  以上两句  ----
        
        doReturn("hi").when(exampleSpy).doSomething();
        System.out.println(exampleSpy.doSomething());
        // 输出:
        // hi
    }
}

场景二:@BeforeEach+@Spy



import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;

import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;


public class Example2Test {
    @Spy
    private Example eSpy = new Example();

    @BeforeEach // juint 5 的注解
    public void init() {
        MockitoAnnotations.openMocks(this);
    }

    @Test
    public void test1() {
        doReturn("hi").when(eSpy).doSomething();
        System.out.println(eSpy.doSomething());
        // 输出:
        // hi
    }

    @Test
    public void test2() {
        when(eSpy.doSomething()).thenReturn("ni hao");
        System.out.println(eSpy.doSomething());
        // 输出:
        // doSomething (来自doSomething方法的真实调用里打印的日志)
        // ni hao
    }
}

    场景三:@ExtendWith+@Mock

    
    
    import org.junit.jupiter.api.Test;
    import org.junit.jupiter.api.extension.ExtendWith;
    import org.mockito.Mock;
    import org.mockito.junit.jupiter.MockitoExtension;
    
    import static org.mockito.Mockito.doReturn;
    import static org.mockito.Mockito.when;
    
    
    @ExtendWith(MockitoExtension.class) // JUnit 5 可以使用扩展 @ExtendWith(MockitoExtension.class) 自动将@Spy的对象初始化为spy对象
    public class Example3Test {
        @Mock
        private Example eSpy = new Example();
    
        @Test
        public void test1() {
            doReturn("hi").when(eSpy).doSomething();
            System.out.println(eSpy.doSomething());
            // 输出:
            // hi
        }
    
        @Test
        public void test2() {
            when(eSpy.doSomething()).thenReturn("ni hao");
            System.out.println(eSpy.doSomething());
            // 输出:
            // ni hao
        }
    }
    

    场景四:@BeforeEach+@Mock

    
    
    import org.junit.jupiter.api.BeforeEach;
    import org.junit.jupiter.api.Test;
    import org.mockito.Mock;
    import org.mockito.MockitoAnnotations;
    
    import static org.mockito.Mockito.doReturn;
    import static org.mockito.Mockito.when;
    
    
    public class Example4Test {
        @Mock
        private Example eSpy = new Example();
    
        @BeforeEach // juint 5 的注解
        public void init() {
            MockitoAnnotations.openMocks(this);
        }
    
        @Test
        public void test1() {
            doReturn("hi").when(eSpy).doSomething();
            System.out.println(eSpy.doSomething());
            // 输出:
            // hi
        }
    
        @Test
        public void test2() {
            when(eSpy.doSomething()).thenReturn("ni hao");
            System.out.println(eSpy.doSomething());
            // 输出:
            // ni hao
        }
    }
    

    加载SpringBoot上下文

    junit5可以不用加@RunWith了

    
    
    import org.example.springbootdemo.spring.SpringExample;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    
    // junit5
    @SpringBootTest // 启动完整 SpringBoot 应用上下文
    public class Spring3Test {
        @Autowired
        private SpringExample springExample;
    
        @Test
        public void test1() {
            springExample.voidFun();
            // 输出:
            // hello
        }
        
    }
    

    SpringBoot+@MockBean

    
    
    import org.example.springbootdemo.spring.SpringExample;
    import org.junit.jupiter.api.Test;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.boot.test.mock.mockito.MockBean;
    
    import static org.mockito.Mockito.when;
    
    // junit5
    @SpringBootTest // 启动完整 SpringBoot 应用上下文
    public class Spring3Test {
        @MockBean // 替换 Spring 容器中的 springExample bean
        private SpringExample springExample;
    
        @Test
        public void test1() {
            springExample.voidFun();
            // 没有任何输出
        }
    
        @Test
        public void test2() {
            when(springExample.doSomething()).thenReturn("hi");
            System.out.println(springExample.doSomething());
            // 输出:
            // hi
        }
    }
    

    testng

    被测试类

    import org.springframework.stereotype.Component;
    
    @Component
    public class SpringExample {
        public void voidFun() {
            System.out.println("hello");
        }
        public String doSomething() {
            System.out.println("do something");
            return "hello something";
        }
    }

    testng+mockito

    
    import org.mockito.MockitoAnnotations;
    import org.mockito.Spy;
    import org.testng.annotations.BeforeClass;
    import org.testng.annotations.Test;
    
    import static org.mockito.Mockito.doReturn;
    import static org.mockito.Mockito.when;
    
    public class Example7Test {
        @Spy
        private Example eSpy = new Example();
    
        @BeforeClass // 这个是testng的
        public void init() {
    //        MockitoAnnotations.initMocks(this); // 这种初始化方式已经被废弃了
            MockitoAnnotations.openMocks(this); // 作用:将@Spy或者@Mock的对象初始化为spy或者mock对象
        }
        
        @Test // 这个是testng的
        public void test1() {
            doReturn("hi").when(eSpy).doSomething();
            System.out.println(eSpy.doSomething());
            // 输出:
            // hi
        }
    
        @Test
        public void test2() {
            when(eSpy.doSomething()).thenReturn("ni hao");
            System.out.println(eSpy.doSomething());
            // 输出:
            // doSomething (来自doSomething方法的真实调用里打印的日志)
            // ni hao
        }
    }
    

    加载SpringBoot上下文

    注意要继承 AbstractTestNGSpringContextTests。

    
    
    import org.example.springbootdemo.spring.SpringExample;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
    import org.testng.annotations.Test;
    
    // testng
    @SpringBootTest // 启动完整 SpringBoot 应用上下文
    public class Spring5Test extends AbstractTestNGSpringContextTests {
        @Autowired // 和junit4  junit5 的使用一样,只是@Test改为testng的
        private SpringExample springExample;
    
        @Test
        public void test1() {
            springExample.voidFun();
            // 输出:
            // hello
        }
    }
    

    SpringBoot+@MockBean

    @MockBean有点特殊,必须要加@TestExecutionListeners(MockitoTestExecutionListener.class),也不用显示MockitoAnnotations.openMocks(this);

    原因暂时还不知道

    
    
    import org.example.springbootdemo.spring.SpringExample;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.boot.test.mock.mockito.MockBean;
    import org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener;
    import org.springframework.test.context.TestExecutionListeners;
    import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
    import org.testng.annotations.Test;
    
    import static org.mockito.Mockito.when;
    
    // testng
    @SpringBootTest
    // 不加@TestExecutionListeners的话@MockBean的对象是null
    // 因为Spring集成TestNG的抽象类: AbstractTestNGSpringContextTests 上并没加关于Mockito相关的测试监听器,所以就没有运用上MockBean的注解功能。
    // 官方也有讨论过这个issue,但官方并不打算集成进来,所以叫大家自己加个Listener,因为Mockito这样的产品会有很多种。
    @TestExecutionListeners(MockitoTestExecutionListener.class) // 注意要加上这个!!!
    public class Spring6Test extends AbstractTestNGSpringContextTests {
        @MockBean
        private SpringExample springExample;
    
    //    @BeforeClass // 这里不需要显示配这个,原因暂时还不知道,在这个测试类中,配这个和不配,没有影响这里的用例结果
    //    public void init() {
    //        MockitoAnnotations.openMocks(this);
    //    }
    
        @Test
        public void test1() {
            System.out.println(springExample.doSomething());
            // 没有任何输出
        }
    
        @Test
        public void test2() {
            when(springExample.doSomething()).thenReturn("hi");
            System.out.println(springExample.doSomething());
            // 输出:
            // hi
        }
    }
    

    SpringBoot+@InjectMocks+@Mock

    
    
    import org.example.springbootdemo.spring.SpringExample;
    import org.example.springbootdemo.spring.SpringSubExample;
    import org.mockito.InjectMocks;
    import org.mockito.Mock;
    import org.mockito.MockitoAnnotations;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener;
    import org.springframework.test.context.TestExecutionListeners;
    import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
    import org.testng.annotations.BeforeClass;
    import org.testng.annotations.Test;
    
    import static org.mockito.Mockito.when;
    
    // testng + @Mock + @InjectMocks
    @SpringBootTest
    //@TestExecutionListeners(MockitoTestExecutionListener.class) // 这里和 MockitoAnnotations.openMocks() 二选一即可
    public class Spring7Test extends AbstractTestNGSpringContextTests {
        @InjectMocks
        private SpringExample springExample;
    
        @Mock // 将 springSubExample 的mock bean 替换到 springExample bean 中的对应属性bean
        private SpringSubExample springSubExample;
    
        @BeforeClass // 这里和 @TestExecutionListeners(MockitoTestExecutionListener.class) 二选一即可
        public void init() {
            MockitoAnnotations.openMocks(this);
        }
    
        @Test
        public void test1() {
            System.out.println(springExample.doSomething());
            // 输出真实执行日志
        }
    
        @Test
        public void test2() {
            when(springSubExample.doSub()).thenReturn("hi");
            System.out.println(springExample.doSub());
            // 输出:
            // hi
        }
    }
    

    spy和mock的区别

    被测试类

    public class ExampleElse {
        public String doSomething() {
            System.out.println("doSomething");
            return "hello";
        }
    
        public void voidFun() {
            System.out.println("voidFun");
        }
    
        public String doSomethingElse() {
            voidFun();
            System.out.println("doSomethingElse");
            return "good";
        }
    }

    spy

    
    
    import org.mockito.MockitoAnnotations;
    import org.mockito.Spy;
    import org.testng.annotations.BeforeClass;
    import org.testng.annotations.Test;
    
    import static org.mockito.Mockito.doNothing;
    
    
    public class ExampleElse1Test {
        @Spy
        private ExampleElse eSpy = new ExampleElse();
    
        @BeforeClass
        public void init() {
            MockitoAnnotations.openMocks(this);
        }
    
        @Test
        public void test1() {
            eSpy.doSomethingElse();
            // 输出:
            // voidFun
            // doSomethingElse
        }
    
        @Test
        public void test2() {
            // 注意doNothing只能对返回void的方法使用。如果是有返回值的方法可以用doReturn().when()h或则when().thenReturn()
            doNothing().when(eSpy).voidFun(); // 表示eSpy调用voidFun()方法时,什么都不做,相当于没有调用这个方法
            eSpy.doSomethingElse(); // doSomethingElse()这个方法里本来会调用voidFun()方法,有了上面的打桩,这里就跳过调用
            // 输出:
            // doSomethingElse
        }
    }
    

    mock

    
    
    import org.mockito.Mock;
    import org.mockito.MockitoAnnotations;
    import org.testng.annotations.BeforeClass;
    import org.testng.annotations.Test;
    
    import static org.mockito.Mockito.CALLS_REAL_METHODS;
    import static org.mockito.Mockito.mock;
    
    
    public class ExampleElse2Test {
        @Mock
        private ExampleElse eMock;
    
        @BeforeClass
        public void init() {
            MockitoAnnotations.openMocks(this);
        }
    
        @Test
        public void test1() {
            eMock.doSomethingElse();
            // 没有任何输出
            // 这是因为 eMock 是一个 mock 对象(由 Mockito 创建的代理对象),
            // 并且没有对其方法行为进行明确的 stub(定义模拟行为)。默认情况下,Mockito 创建的 mock
            // 对象不会执行任何真实逻辑,也不会输出任何日志,因为 Mockito 会替换真实方法调用为空的 mock 行为。
    
            // 用spy则可以执行真实逻辑,这是二者的区别
        }
    
        @Test
        public void test2() {
            // 通过 Mockito 的 CALLS_REAL_METHODS,让 mock 对象的所有方法调用默认执行真实逻辑。
            ExampleElse exampleElseMock = mock(ExampleElse.class, CALLS_REAL_METHODS); 
            exampleElseMock.doSomethingElse();
            // 输出:
            // voidFun
            // doSomethingElse
        }
    }
    

    pom依赖

             <dependency>
                <groupId>org.testng</groupId>
                <artifactId>testng</artifactId>
                <version>6.4</version>
                <scope>test</scope>
            </dependency>
            <!-- Spring Boot Web 依赖 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <version>2.7.8</version>
            </dependency>
            <!-- Spring Boot Test 依赖 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <version>2.7.8</version>
                <scope>test</scope>
            </dependency>

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值