【JAVA】单元测试的简单应用

单元测试是验证软件中最小可测试部分正确性的自动化测试。在Java中,单元测试通常针对类的方法或函数进行。以下是单元测试的一般写法,以及一些常用的单元测试框架。

1. 准备工作

在开始编写单元测试之前,需要确保项目中包含了单元测试框架。Java中常用的单元测试框架有:

  • JUnit: 最流行的Java单元测试框架。
  • TestNG: 功能强大的测试框架,与JUnit兼容但提供了更多特性。
  • Mockito: 主要用于模拟对象和行为的工具。

2. 创建测试类

测试类通常放在与源代码相同的包中,但位于不同的源集(source set)里。测试类的命名约定通常是在被测试类名后加上"Test"后缀。

3. 编写测试方法

测试方法通常遵循以下命名约定:test + 方法描述,必须使用public访问修饰符,并且是void返回类型。JUnit 4使用注解@Test来标识测试方法。

4. 断言(Assertions)

断言是单元测试中的核心,用于验证代码的预期输出。JUnit提供了多种断言方法,如:

  • assertEquals: 验证两个值是否相等。
  • assertTrue: 验证一个条件是否为真。
  • assertFalse: 验证一个条件是否为假。
  • assertNotNull: 验证对象不为null。
  • assertNull: 验证对象为null。

示例:使用JUnit 4编写单元测试

假设我们有以下简单的加法类:

public class Calculator {
    public int add(int a, int b) {
        return a + b;
    }
}

对应的单元测试可能如下:

import static org.junit.Assert.assertEquals;
import org.junit.Test;

public class CalculatorTest {

    @Test
    public void testAdd() {
        Calculator calculator = new Calculator();
        assertEquals("10 + 5 must equal 15", 15, calculator.add(10, 5));
    }
    
    @Test
    public void testAddZero() {
        Calculator calculator = new Calculator();
        assertEquals("Adding zero must return the same number", 5, calculator.add(5, 0));
    }
    
    @Test(expected = IllegalArgumentException.class)
    public void testAddNegative() {
        Calculator calculator = new Calculator();
        calculator.add(-1, 5);
    }
}

使用Mockito进行单元测试

当测试涉及与其他对象交互或需要模拟外部依赖时,可以使用Mockito来创建模拟对象。

import static org.mockito.Mockito.*;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

public class ServiceTest {

    @Mock
    private Dependency dependency;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testSomeBehavior() {
        Service service = new Service(dependency);
        
        // 设置模拟行为
        when(dependency.someMethod()).thenReturn("expected value");
        
        // 执行测试
        String result = service.someBehavior();
        
        // 验证方法调用
        verify(dependency).someMethod();
        
        // 断言结果
        assertEquals("Method should return expected value", "expected value", result);
    }
}

实例1:验证方法调用

假设我们有一个UserService类,它依赖于一个UserRepository接口来获取用户信息。

public interface UserRepository {
    User findUserById(int id);
}

public class UserService {
    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public User getUser(int id) {
        return userRepository.findUserById(id);
    }
}

 

单元测试使用Mockito验证getUser方法是否正确调用了UserRepositoryfindUserById方法。

import static org.mockito.Mockito.*;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

public class UserServiceTest {

    @Mock
    private UserRepository userRepository;

    private UserService userService;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        userService = new UserService(userRepository);
    }

    @Test
    public void testGetUser() {
        User expectedUser = new User(1, "John Doe");
        when(userRepository.findUserById(anyInt())).thenReturn(expectedUser);

        User user = userService.getUser(1);

        verify(userRepository).findUserById(1);
        assertEquals("The returned user should be the expected user", expectedUser, user);
    }
}

实例2:测试异常处理

假设UserRepositoryfindUserById方法可能抛出一个自定义异常UserNotFoundException

public class UserNotFoundException extends Exception {
    public UserNotFoundException(int id) {
        super("User with id " + id + " not found.");
    }
}

// UserService类保持不变

单元测试验证当用户未找到时,UserService是否正确地将异常传递出去。

@Test(expected = UserNotFoundException.class)
public void testGetUserWhenUserNotFound() {
    when(userRepository.findUserById(anyInt())).thenThrow(new UserNotFoundException(1));

    userService.getUser(1);
}

实例3:使用Mockito验证顺序

假设OrderService类需要按照特定的顺序调用两个依赖项PaymentServiceEmailService

public class OrderService {
    private final PaymentService paymentService;
    private final EmailService emailService;

    public OrderService(PaymentService paymentService, EmailService emailService) {
        this.paymentService = paymentService;
        this.emailService = emailService;
    }

    public void processOrder(Order order) {
        paymentService.processPayment(order);
        emailService.sendConfirmationEmail(order);
    }
}

单元测试验证processOrder方法中服务的调用顺序。

import static org.mockito.Mockito.*;

@Test
public void testProcessOrder() {
    @Mock
    PaymentService paymentService;
    @Mock
    EmailService emailService;
    OrderService orderService = new OrderService(paymentService, emailService);
    Order order = new Order();

    orderService.processOrder(order);

    verify(paymentService).processPayment(order);
    verify(emailService).sendConfirmationEmail(order);
    // 验证调用顺序
    verify(paymentService, Mockito.ordering()).processPayment(order);
    verify(emailService, Mockito.ordering()).sendConfirmationEmail(order);
}

实例4:使用Spy进行部分模拟

假设我们想测试NetworkService类的真实行为,但同时要模拟一个方法。

public class NetworkService {
    public String sendRequest(String url) {
        // 实际发送网络请求的代码
        return "response";
    }
}

单元测试中,我们使用Spy来模拟sendRequest方法,而其他方法则使用真实实现。

@Test
public void testNetworkService() {
    NetworkService spyService = Mockito.spy(new NetworkService());
    doReturn("mocked response").when(spyService).sendRequest(anyString());

    String response = spyService.sendRequest("http://example.com");

    assertEquals("The response should be mocked", "mocked response", response);
    // 验证sendRequest是否被调用一次
    verify(spyService).sendRequest("http://example.com");
}

注意事项

  • 测试应该独立于彼此运行,不依赖于外部资源如数据库或网络服务。
  • 测试应该快速执行,以便频繁运行。
  • 测试代码应该具有可读性,易于理解。
  • 测试覆盖率是一个有用的指标,但100%的覆盖率并不总是必要的或最佳的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/598308.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

04-18 周四 为LLM_inference项目配置GitHub CI过程记录

04-18 周四 为LLM_inference项目配置GitHub CI过程记录 时间版本修改人描述2024年4月18日10:30:13V0.1宋全恒新建文档 简介和相关文档 04-15 周一 GitHub仓库CI服务器配置过程文档actions-runner 是托管与GitHub上的仓库,下载最新的客户端程序即可。self hosted r…

多C段的美国站群服务器有什么用途?

多C段的美国站群服务器有什么用途? 多C段的美国站群服务器是一种常见的网络运营策略,其用途主要体现在以下几个方面: 多C段的美国站群服务器有什么用途? 1. 提高站点排名和流量 部署多个站点在不同的C段IP地址上,可以通过不同的IP地址发布…

BGP协议应用:SW1、SW2、SW3、RT1、RT2之间运行BGP协议

8.SW1、SW2、SW3、RT1、RT2之间运行BGP协议,SW1、SW2、RT1 AS号65001、RT2 AS号65002、SW3 AS号65003。 (1)SW1、SW2、SW3、RT1、RT2之间通过Loopback1建立IPv4 BGP邻居。SW1和SW2之间财务通过Loopback2建立IPv4 BGP邻居,SW1和SW2的Loopback2互通采用静态路由。 (2)SW1…

运行一个jar包

目录 传送门前言一、Window环境二、Linux环境1、第一步:环境配置好,安装好jdk2、第二步:打包jar包并上传到Linux服务器3、第三步:运行jar包 三、docker环境1、Linux下安装docker和docker compose2、Dockerfile方式一运行jar包2.1、…

优思学院|HR部门如何制定公司的精益六西格玛培训计划?

在许多企业中,精益六西格玛作为一种提升效率和质量的重要方法论,越来越受到重视。HR部门在推广和实施精益六西格玛培训计划中其实也扮演着关键角色。以下是HR部门可以采取的几个步骤,以有效地制定和实施这样的培训计划。 1. 需求分析 首先&…

人工智能学习+Python的优势

1.人工智能的发展阶段 1.1 强人工智能: 1.2 弱人工智能: 2.符号学习 符号学习的本质就是:规定好的逻辑和顺序,根据这个模板告诉机器接下来需要做什么,遵循if...then原则——>缺点:不能根据新的场景…

本地主机访问服务器的Redis -- 配置 ssh 端口转发

前言 在进行Java开发时,高度的依赖 Windows 上的开发软件 idea ,那么我们想访问位于服务器上的 redis 怎么办呢?在平时我们想访问位于服务器上的程序,只需要开放它的端口即可,比如我们创建的网站,比如 tomc…

Go通过CRUD实现学生管理系统

虽然这个项目没有什么含金量,但是可以熟悉go的语法和go开发项目的一般流程 项目结构 项目实现了五个功能: (1)增加一个学生 (2)删除一个学生 (3)修改一个学生的信息 (4&#xf…

基于微信小程序的图书馆预约系统的设计与实现

个人介绍 hello hello~ ,这里是 code袁~💖💖 ,欢迎大家点赞🥳🥳关注💥💥收藏🌹🌹🌹 🦁作者简介:一名喜欢分享和记录学习的…

【一起深度学习吧!!!!!】24/05/03

卷积层里的多输入输出通道 1、 多输入通道:代码演示: 多输出通道:代码实现: 1、 多输入通道: 当输入包含多个通道时,需要构造一个输入通道与之相等的卷积核,以便进行数据互相关计算。 例如李沐…

责任链模式和观察者模式

1、责任链模式 1.1 概述 在现实生活中,常常会出现这样的事例:一个请求有多个对象可以处理,但每个对象的处理条件或权限不同。例如,公司员工请假,可批假的领导有部门负责人、副总经理、总经理等,但每个领导…

XMall-Front:基于Vue.js的XMall商城前台页面的开发实践

XMall-Front:基于Vue.js的XMall商城前台页面的开发实践 摘要 随着电子商务的蓬勃发展,用户体验逐渐成为决定电商平台成功与否的关键因素。作为XMall商城项目的一部分,XMall-Front是基于Vue.js的前端页面开发,其目标是为用户提供…

《深入解析WIndows操作系统》第10章读书笔记

1、大页面和小页面:虚拟地址空间被划分成以页面为单位,这是因为硬件内存管理单元在页面的粒度上,将虚拟地址转译为物理地址。Windows支持两种页面尺寸:大页面和小页面,根据处理器体系结构不同,实际尺寸值有…

HAL PWM 配置 占空比 频率 stm32 学习笔记

title: HALPWM配置占空比频率 tags: STM32ClionHal 1.STM32CubeMX学习笔记(13)——PWM输出(呼吸灯)使用 2.STM32标准库HAL库 | 高精度动态调节PWM输出频率占空比 看你cubemx 里面的配置时钟频率是多少 参照第二篇文章描述修改 下面俩个参数就行 uin…

SAP生产订单常用状态以及

常用系统状态: 状态 状态 CRTD 已建立 REL 已核发 CNF 已确认 PCNF 已部份确认 DLV 已交货 DLT 删除指示码 LKD 已锁住 TECO 技术完成 GMPS 已发料 关闭 关闭 工单结案前的生产报表分析 路径:后勤系统- 生产- 现场控制- 信息系统-订单信息系…

NFS共享存储服务配置实践

一、NFS 1.NFS定义 NFS(Network File System)网络文件服务:基于TCP/IP传输的网络文件系统协议,NFS服务的实现依赖于RPC(Remote Process Call)远端过程调用:通过使用NFS协议,客户机…

02、Kafaka 简介

02、Kafka 简介 1、 Kafka 简介 Apache Kafka 是一个分布式的发布-订阅消息系统,最初由 LinkedIn 公司开发,并在 2010 年贡献给了 Apache 软件基金会,成为一个顶级开源项目。Kafka 设计之初是为了满足高吞吐量、可扩展性、持久性、容错性以…

VALSE 2024特邀报告内容解析|多模态视觉融合方法:是否存在性能极限?

2024年视觉与学习青年学者研讨会(VALSE 2024)于5月5日到7日在重庆悦来国际会议中心举行。本公众号将全方位地对会议的热点进行报道,方便广大读者跟踪和了解人工智能的前沿理论和技术。欢迎广大读者对文章进行关注、阅读和转发。文章是对报告人…

高频次的低价监测如何实现

品牌在做控价的过程中,需要对渠道中的低价数据进行监测,但价格数据变化快,涉及的促销信息也很多,如何将这些变化的数据监测到位,同时对于低价的凭证还要截图留证,以便有效的进行渠道治理,这就需…

python菜鸟级安装手册

python安装教程 电脑-右键-属性,确认系统类型和版本号,比如本案例系统是64位 win10 点击python官网,进行下载 适用于 Windows 的 Python 版本 |Python.org 选择第一个安装程序64位即可满足需要, 嵌入式程序包是压缩包版本&…