• 【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编写单元测试

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

    1. public class Calculator {
    2. public int add(int a, int b) {
    3. return a + b;
    4. }
    5. }

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

    1. import static org.junit.Assert.assertEquals;
    2. import org.junit.Test;
    3. public class CalculatorTest {
    4. @Test
    5. public void testAdd() {
    6. Calculator calculator = new Calculator();
    7. assertEquals("10 + 5 must equal 15", 15, calculator.add(10, 5));
    8. }
    9. @Test
    10. public void testAddZero() {
    11. Calculator calculator = new Calculator();
    12. assertEquals("Adding zero must return the same number", 5, calculator.add(5, 0));
    13. }
    14. @Test(expected = IllegalArgumentException.class)
    15. public void testAddNegative() {
    16. Calculator calculator = new Calculator();
    17. calculator.add(-1, 5);
    18. }
    19. }

    使用Mockito进行单元测试

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

    1. import static org.mockito.Mockito.*;
    2. import org.junit.Before;
    3. import org.junit.Test;
    4. import org.mockito.Mock;
    5. import org.mockito.MockitoAnnotations;
    6. public class ServiceTest {
    7. @Mock
    8. private Dependency dependency;
    9. @Before
    10. public void setUp() {
    11. MockitoAnnotations.initMocks(this);
    12. }
    13. @Test
    14. public void testSomeBehavior() {
    15. Service service = new Service(dependency);
    16. // 设置模拟行为
    17. when(dependency.someMethod()).thenReturn("expected value");
    18. // 执行测试
    19. String result = service.someBehavior();
    20. // 验证方法调用
    21. verify(dependency).someMethod();
    22. // 断言结果
    23. assertEquals("Method should return expected value", "expected value", result);
    24. }
    25. }

    实例1:验证方法调用

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

    1. public interface UserRepository {
    2. User findUserById(int id);
    3. }
    4. public class UserService {
    5. private final UserRepository userRepository;
    6. public UserService(UserRepository userRepository) {
    7. this.userRepository = userRepository;
    8. }
    9. public User getUser(int id) {
    10. return userRepository.findUserById(id);
    11. }
    12. }

     

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

    1. import static org.mockito.Mockito.*;
    2. import org.junit.Before;
    3. import org.junit.Test;
    4. import org.mockito.Mock;
    5. import org.mockito.MockitoAnnotations;
    6. public class UserServiceTest {
    7. @Mock
    8. private UserRepository userRepository;
    9. private UserService userService;
    10. @Before
    11. public void setUp() {
    12. MockitoAnnotations.initMocks(this);
    13. userService = new UserService(userRepository);
    14. }
    15. @Test
    16. public void testGetUser() {
    17. User expectedUser = new User(1, "John Doe");
    18. when(userRepository.findUserById(anyInt())).thenReturn(expectedUser);
    19. User user = userService.getUser(1);
    20. verify(userRepository).findUserById(1);
    21. assertEquals("The returned user should be the expected user", expectedUser, user);
    22. }
    23. }

    实例2:测试异常处理

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

    1. public class UserNotFoundException extends Exception {
    2. public UserNotFoundException(int id) {
    3. super("User with id " + id + " not found.");
    4. }
    5. }
    6. // UserService类保持不变

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

    1. @Test(expected = UserNotFoundException.class)
    2. public void testGetUserWhenUserNotFound() {
    3. when(userRepository.findUserById(anyInt())).thenThrow(new UserNotFoundException(1));
    4. userService.getUser(1);
    5. }

    实例3:使用Mockito验证顺序

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

    1. public class OrderService {
    2. private final PaymentService paymentService;
    3. private final EmailService emailService;
    4. public OrderService(PaymentService paymentService, EmailService emailService) {
    5. this.paymentService = paymentService;
    6. this.emailService = emailService;
    7. }
    8. public void processOrder(Order order) {
    9. paymentService.processPayment(order);
    10. emailService.sendConfirmationEmail(order);
    11. }
    12. }

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

    1. import static org.mockito.Mockito.*;
    2. @Test
    3. public void testProcessOrder() {
    4. @Mock
    5. PaymentService paymentService;
    6. @Mock
    7. EmailService emailService;
    8. OrderService orderService = new OrderService(paymentService, emailService);
    9. Order order = new Order();
    10. orderService.processOrder(order);
    11. verify(paymentService).processPayment(order);
    12. verify(emailService).sendConfirmationEmail(order);
    13. // 验证调用顺序
    14. verify(paymentService, Mockito.ordering()).processPayment(order);
    15. verify(emailService, Mockito.ordering()).sendConfirmationEmail(order);
    16. }

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

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

    1. public class NetworkService {
    2. public String sendRequest(String url) {
    3. // 实际发送网络请求的代码
    4. return "response";
    5. }
    6. }

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

    1. @Test
    2. public void testNetworkService() {
    3. NetworkService spyService = Mockito.spy(new NetworkService());
    4. doReturn("mocked response").when(spyService).sendRequest(anyString());
    5. String response = spyService.sendRequest("http://example.com");
    6. assertEquals("The response should be mocked", "mocked response", response);
    7. // 验证sendRequest是否被调用一次
    8. verify(spyService).sendRequest("http://example.com");
    9. }

    注意事项

    • 测试应该独立于彼此运行,不依赖于外部资源如数据库或网络服务。
    • 测试应该快速执行,以便频繁运行。
    • 测试代码应该具有可读性,易于理解。
    • 测试覆盖率是一个有用的指标,但100%的覆盖率并不总是必要的或最佳的。
  • 相关阅读:
    Spring【SpringAOP(通知类型、切点表达式 、多切面配置 、注解配置AOP、原生Spring实现AOP)】(六)-全面详解(学习总结---从入门到深化)
    RabbitMQ真实生产故障问题还原与分析
    vuejs3+elementPlus后台管理系统,左侧菜单栏制作,跳转、默认激活菜单
    【C++ STL】哈希 Hash(闭散列、开散列介绍及其实现)
    【每日一记】OSPF卡在2-way状态、OSPF卡在Exstart状态解析
    Hive安装教程-Hadoop集成Hive
    JVM的运行时数据区
    云计算实验3 基于Scala编程语言的Spark数据预处理实验
    vue3使用ref和reactive
    Java教程:RocketMq集群消息核心知识与SpringBoot整合并实现生产者与消费者
  • 原文地址:https://blog.csdn.net/xiaosemei/article/details/138521265