Java单元测试

目录

一、Junit

二 、Mockito

1.什么是 mock 测试

2.Mockito 中常用的方法

3.Mockito 中常用的注解

三、举例


一、Junit

依赖:

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.7</version>
    </dependency>
</dependencies>

注解:

注解作用
@Test用于修饰需要执行的测试方法,一般用来对某些对象进行初始化, 或者加载驱动等操作
@Before修饰的方法会在任意测试方法之前被自动执行,一般写的是正常的业务逻辑
@After修饰的方法会在任意测试方法执行之后自动被执行,一般是用来释放或关闭资源的

示例:

// 实际方法类
public class Cal {
    public int add(int a,int b) {
        return a + b;
    }
 
    public int sub(int a,int b){
        return a - b;
    }
}
// 测试类
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
 
public class CalTest {
    @Before
    public void init(){
        System.out.println("init...");
    }
 
    @After
    public void close(){
        System.out.println("close...");
    }
 
    @Test
    public void testAdd(){
        Cal c = new Cal();
        int result = c.add(1,2);
        Assert.assertEquals(3,result);
        System.out.println("testAdd...");
    }
 
    @Test
    public void testSub(){
        Cal c = new Cal();
        int result = c.sub(1,2);
        Assert.assertEquals(2,result);
        System.out.println("testSub...");
    }
}

testAdd 的结果:

testSub 的结果:

参考:

  1. Java中junit单元测试_贺贺学编程的博客-CSDN博客_java junit单元测试

  1. Java基础——Junit单元测试_小城老街的博客-CSDN博客

二 、Mockito

1.什么是 mock 测试

Mock 测试可以理解为模拟测试,就是在测试中模拟真实对象(模拟入参、指定返回结果等),从而达到不依赖实际环境完成测试(如用户入参、查数据库等)。

依赖:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>4.3.1</version>
    <scope>test</scope>
</dependency>
​
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.8.2</version>
    <scope>test</scope>
</dependency>

2.Mockito 中常用的方法

mock 方法 :mock 一个类

// 示例
Random random = Mockito.mock(Random.class);

verify 方法:对 mock 出来的对象进行行为验证

// 示例:验证方法是否被调用
@Test
public void justTest(){
    Random random = Mockito.mock(Random.class);      
    System.out.println(random.nextInt());
    Mockito.verify(random).nextInt();
}

Verify 配合 time() 方法,可以校验某些操作发生的次数。

@Test
public void justTest(){
    Random random = Mockito.mock(Random.class);
    System.out.println(random.nextInt());
    Mockito.verify(random,Mockito.times(2)).nextInt();
}

更多用法讲解:Mockito Verify的使用_BlueZhang521的博客-CSDN博客_mockito.verify


断言 Assertions 类

// 示例:
Random random = Mockito.mock(Random.class);
Assertions.assertEquals(100, random.nextInt());

输出结果:

org.opentest4j.AssertionFailedError: expected: <100> but was: <0>

Expected :100

Actual :0

当使用 mock 对象方法时,如果不对其行为进行定义(指定结果),则返回值为返回类型的默认值。


打桩

打桩可以理解为:为 mock 对象指定行为结果。

方法含义
when().thenReturn()Mock 对象在触发指定行为后返回指定值
when().thenThrow()Mock 对象在触发指定行为后抛出指定异常
when().doCallRealMethod()Mock 对象在触发指定行为后调用真实的方法

when().thenReturn() 示例:

@Test
void check() {
    Random random = Mockito.mock(Random.class);
    Mockito.when(random.nextInt()).thenReturn(100);
    Assertions.assertEquals(100, random.nextInt());
}

和上面没有进行打桩对比,此处使用 thenReturn() 方法定义了返回结果,所以使用断言判断结果正确,测试通过。

3.Mockito 中常用的注解

@Mock

@Mock
private Random random;
​
@Test
void check() {
    MockitoAnnotations.openMocks(this);
    Mockito.when(random.nextInt()).thenReturn(100);
    Assertions.assertEquals(100, random.nextInt());
}

使用 @Mock 注解代替了 Mockito.Mock() 方法,等效于下面的代码。

@Test
void check() {
    Random random = Mockito.mock(Random.class);
    Mockito.when(random.nextInt()).thenReturn(100);
    Assertions.assertEquals(100, random.nextInt());
}

注意:@Mock 注解需要搭配 MockitoAnnotations.openMocks(testClass) 方法一起使用。

@BeforeEach 与 @BeforeAfter

在 Junit5 中,@Before@After 注解被 @BeforeEach@AfterEach 所替代

Mockito.spy() 方法和 @Spy 注解

示例:

// Mockito.spy()方法
@Test
void check() {
    UtilTest utilTest = Mockito.spy(new UtilTest());
    int res = utilTest.add(1, 2);
    Assertions.assertEquals(3, res);
}
​
// @Spy 注解
@Spy
private UtilTest utilTest;
@Test
void check() {
    MockitoAnnotations.openMocks(this);
    int res = utilTest.add(1, 2);
    Assertions.assertEquals(3, res);
}

spy() 方法与 mock() 方法的区别:

① spy() 方法的参数是对象实例,mock 的参数是 class;

② 被 spy 的对象会走真实的方法,而 mock 对象不会(即 spy 对象会走实际 add() 方法,1+2=3;而 mock 对象不会,1+2 返回默认值 0)

三、举例

主要是为了理解各种方法怎么用,如注解、静态方法、打桩等,忽略细节。

// 服务类
public class RegistrationServiceImpl implements RegistrationService {

    SalesDao salesDao = new SalesDao();
    UserDao userDao = new UserDao();

    @Override
    public User register(String name, String phone) throws Exception {
        // 参数校验
        if (name == null || name.length() == 0) {
            throw new ValidationException("number 不能为空");
        }
        if (phone == null || !isValid(phone)) {
            throw new ValidationException("phone 格式错误");
        }
        
        // 通过手机号获取区域编号
        String areaCode = FindUtils.getAreaCode(phone);
        // 通过手机号获取运营商编号
        String operatorCode = FindUtils.getOperatorCode(phone);

        User user;
        try {
            // 通过 区域编号、运营商编号 找人
            SalesRep rep = salesDao.findRep(areaCode, operatorCode);
            // 将找到的人存储
            user = userDao.save(name, phone, rep.getRepId());
        } catch (SQLException e) {
            throw new DAOException("SQLException thrown " + e.getMessage());
        }
        return user;
    }

    private boolean isValid(String phone) {
        String pattern = "^(13[0-9]|14[5|7]|15[0|1|2|3|4|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\\d{8}$";
        boolean flag = phone.matches(pattern);
        return flag;
    }
}
// 测试类
class RegistrationServiceImplTest {

    @InjectMocks
    @Spy
    private RegistrationServiceImpl registrationService;
    @Mock
    private UserDao userDao;
    @Mock
    private SalesDao salesDao;

    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
    }

    @Test
    void register() throws Exception {
        String name = null;
        String phone = "15071271412";
        try {
            registrationService.register(name, phone);
            Assertions.fail("这里会挂掉");
        } catch (Exception e) {
            Assertions.assertTrue(e instanceof ValidationException);
        }

        name = "一直游到海水变蓝";
        phone = null;
        try {
            registrationService.register(name, phone);
            Assertions.fail("这里会挂掉");
        } catch (Exception e) {
            Assertions.assertTrue(e instanceof ValidationException);
        }

        phone = "15071271412";
        // 静态方法 mockStatic
        MockedStatic<FindUtils> staticService = Mockito.mockStatic(FindUtils.class);
        staticService.when(() -> FindUtils.getAreaCode("15071271412")).thenReturn("a");
        staticService.when(() -> FindUtils.getOperatorCode("15071271412")).thenReturn("b");
        
        // 1.数据库操作正常(不执行 catch 部分代码)
        Mockito.when(salesDao.findRep("a","b")).thenCallRealMethod();
        Mockito.when(userDao.save(name, phone, "Echo")).thenCallRealMethod();
        User user = registrationService.register(name, phone);
        Assertions.assertEquals("Echo", user.getRepId());

        // 2.数据库操作异常(执行 catch 部分代码)
        Mockito.when(userDao.save(name, phone, "Echo")).thenThrow(new SQLException());
        try {
            registrationService.register(name, phone);
        } catch (Exception e) {
            Assertions.assertTrue(e instanceof DAOException);
        }
    }
}

完整代码可以找下面参考链接。

参考:

【Mockito】单元测试如何提升代码覆盖率_哔哩哔哩_bilibili

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值