SpringBoot整合Mockito进行单元测试超全详细教程 JUnit断言 Mockito 单元测试

news2024/12/26 21:29:11

在这里插入图片描述

Mock概念

Mock叫做模拟对象,即用来模拟未被实现的对象可以预先定义这个对象在特定调用时的行为(例如返回值或抛出异常),从而模拟不同的系统状态。

导入Mock依赖

pom文件中引入springboot测试依赖,spring-boot-starter-test中包含了Mockito

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

Mock测试环境和Spring上下文环境

仅使用Mock环境

使用Mock进行测试时候,可以仅仅使用Mock环境,不添加@SpringBootTest,这个时候不会加载Spring上下文(@Autowired等不会起作用),需要手动处理使用@Mock和@InjectMock来处理类之间的依赖关系。

常用注解:
@Mock:

@Mock 是 Mockito 提供的注解,用于生成模拟对象,是创建了一个新的对象。这里的 userDaowebServiceClient 是通过 Mockito 模拟的对象,而不是 Spring 容器中的实际 bean。它们的行为可以通过 when/thenReturn 或其他模拟方法来定义。

注意:当你使用 Mockito@Mock 注解来 mock 一个类时,即使该类已经实现了部分方法,Mockito 也会拦截这些方法的调用。这意味着,默认情况下,Mockito 会模拟这个类的所有方法(包括已经实现的方法),除非你显式定义模拟行为。

因此,当你通过 @Mock 来 mock 一个已经实现部分方法的类时:

  • 如果你调用了已经实现的方法,并且没有为这个方法定义具体的 when/thenReturn 行为,Mockito 会返回 默认值(例如 null、0、false 等),而不会执行类中的实际实现。
  • 如果你想让某些方法在调用时执行它们的实际实现,你需要使用 Mockito 提供的 spy() 功能。
@Spy

@Spy 创建的对象是真实对象的部分模拟(Partial Mock),它会调用对象的真实方法,而只有那些明确模拟的方法才会被替换成模拟的行为。spy() 提供部分模拟功能。未被显式模拟的方法将调用实际实现,已经被模拟的方法则返回预设的模拟值。

在使用 @InjectMocks 时,Mockito 会将 @Mock@Spy 注解的对象注入到被测试的对象中。如果某个依赖项使用了 @Spy,Mockito 会确保被注入的是该对象的部分模拟实现。

spy()mock() 的对比

特性

mock()

spy()

默认行为

模拟所有方法,返回默认值(如 null

调用真实的实现,除非被显式模拟

是否执行实际代码

不执行

执行实际的代码实现

定义模拟行为时是否拦截

会拦截并返回模拟值

如果定义了模拟行为,使用模拟值,没定义则执行实际实现

@InjectMocks:
  • @InjectMocks 是 Mockito 的一个注解,用于将模拟对象(即用 @Mock 创建的对象)注入到被测对象中(这里是 UserService)。
  • Mockito 会创建一个新的 UserService 对象,并将 userDaowebServiceClient 作为依赖注入到这个新的对象中。
  • 与 Spring 容器的行为无关。即使 UserService 已经通过 @Service 注解注册到了 Spring 容器中,在使用 @InjectMocks 时,Mockito 会创建并管理一个全新的 UserService 对象
具体演示:
//UserDao定义
public class UserDao {
    //getUserById有真实的实现
    User getUserById(int userId)
    {
        return new User(1,"张三");
    }
    int saveUser(User user);
}


//WebServiceClient定义
public class WebServiceClient {
    boolean isServiceAvailable();
    String getUserDataFromWebService(int userId);
}


@Service
//UserService依赖于UserDao以及WebServiceClient
public class UserService {
	
    @Autowired
    private UserDao userDao;

    @Autowired
    private WebServiceClient webServiceClient;
    
    //省略操作
}


//可以没有@SpringBootTest
public class UserServiceTest {

    // 使用@Spy部分模拟UserDao对象
    @Spy
    private UserDao userDao;

    // 模拟WebServiceClient对象
    @Mock
    private WebServiceClient webServiceClient;

    // 根据依赖将mock对象注入到UserService中
    @InjectMocks
    private UserService userService;
    
    //必须首先初始化
    @BeforeEach
    public void setUp() {
        //非常重要!!!!!
        MockitoAnnotations.openMocks(this);  // 初始化Mockito
    }
    
    @Test
    void Spytest()
    {
        //模拟saveUser方法,而调用getUserById为其真实实现
        doReturn(1).when(userDao).saveUser(any(User.class));
        //真实行为
        User FoundUser = userDao.getUserById(1);// User张三
        //模拟行为
        userDao.saveUser(FoundUser);// 返回1
        
	}
    
    //省略其他测试方法
}
MockitoAnnotations.openMocks(this)作用:

MockitoAnnotations.openMocks(this) 是用于初始化 @Mock@Spy@InjectMocks 注解的关键步骤。如果没有这行代码,Mockito 将不会创建和初始化这些模拟对象,导致测试失败。

MockitoAnnotations.openMocks(this) 适用于非 Spring 环境下的单元测试。在 Spring Boot 测试中,你通常使用 @MockBean@Autowired,Spring Boot 会自动处理模拟对象的初始化,因此不需要调用这个方法。

搭配Spring上下文

使用Spring上下文需要使用@MockBean来在测试中将 Spring 容器中的某些 bean 替换为 Mockito 模拟的对象,然后可以使用@Autowired处理类之间的依赖关系。

结合 Spring Boot 和 Mockito 的测试方法
  1. 使用 @MockBean:用来替换 Spring 容器中的 bean,模拟它的行为。
  2. 使用 @Autowired:注入 Spring 容器中实际的服务(如 UserService)。
  3. 使用 @SpringBootTest:启动 Spring Boot 的测试上下文。
常用注解:
@MockBean:

@MockBeanSpring Boot 提供的注解,用于创建一个 Mockito 模拟对象,并将它替换到 Spring 上下文中。userDaowebServiceClient 是通过 @MockBean 模拟的对象,而不是真实的对象。这些模拟对象将替换 Spring 容器中的相应 bean,然后可以通过@Autowird自动注入被依赖类中。

@SpyBean:

@SpyBeanSpring Boot 提供的一个注解,专门用于 部分模拟(Partial Mocking) Spring 容器中的 Bean。它的作用是创建一个部分模拟的对象,部分调用真实方法,部分进行模拟(Mock)行为。相比于 Mockito 提供的 @Spy@SpyBean 更加集成到 Spring 环境中,并且允许你将某个 Spring 容器中的 Bean 替换为部分模拟对象。

@SpyBean 的工作原理

  1. 部分模拟@SpyBean 允许你对 Spring 容器中的现有 Bean 进行部分模拟。这意味着模拟的 Bean 会保留其大部分原始行为,只有你明确模拟的部分会改变。
  2. 注入到 Spring 容器中:使用 @SpyBean 时,Spring Boot 会将该部分模拟的 Bean 注入到 Spring 容器中,替换原有的 Bean。
具体演示:
@Service
public class UserService {

    @Autowired
    private UserDao userDao;

    @Autowired
    private WebServiceClient webServiceClient;
    
    //省略操作
}


@SpringBootTest //使用Spring上下文
public class UserServiceTest {

    // 通过Spring容器管理的UserService实例
    @Autowired
    private UserService userService;

    // 部分模拟UserDao对象,替换Spring容器中的UserDao bean
    @SpyBean
    private UserDao userDao;

    // 模拟WebServiceClient对象,替换Spring容器中的WebServiceClient bean
    @MockBean
    private WebServiceClient webServiceClient;
    //被替换的两个Mock对象可以被Spring容器自动注入到userService中
    
    //省略测试方法
}
注意:

当你使用 @MockBean@SpyBean注解时,不需要 调用 MockitoAnnotations.openMocks(this);,因为 @MockBean 是由 Spring Boot 管理的,Spring Boot 会自动初始化并处理 @MockBean 创建的模拟对象。

测试流程:

使用 Mockito 进行测试的一般流程可以分为以下几个步骤:

  1. 设置测试环境:在单元测试中,通过 Mockito 的注解或者方法来创建模拟对象(Mock)。模拟对象是用于替代真实的依赖,以便控制和测试不同的场景。
  2. 定义模拟行为:使用 Mockito 的方法定义模拟对象的方法行为。通常通过 when(...).thenReturn(...) 来模拟返回特定值,或者使用 doThrow() 来模拟异常抛出。
  3. 执行测试代码:编写业务逻辑代码,将模拟对象注入依赖进行测试并断言结果。
  4. 验证行为:使用 verify() 验证方法调用、参数、调用次数等。

1. 准备测试环境

在测试类中准备需要的模拟对象和被测对象。可以通过 @Mock@InjectMocks 注解或者 Mockito.mock() 方法手动创建模拟对象。对于 Spring 项目,还可以使用 @MockBean 来替代 Spring 容器中的 Bean。

2、定义模拟行为:

  1. 使用 when(...).thenReturn(...) 来模拟方法返回值。
  2. 使用 doReturn(...).when(...) 来避免方法的真实调用。
  3. 使用 thenThrow(...)doThrow(...).when(...) 来模拟异常。
  4. 使用 thenAnswer(...) 来处理复杂的动态行为。
  5. 使用 doNothing() 来处理 void 方法。
1. 使用 when(...).thenReturn(...) 来模拟方法返回值

这是最常用的方式。适用于模拟方法调用后需要返回某个特定值的情况。

示例:模拟 UserDaogetUserById() 方法在调用时返回特定的 User 对象。

@Mock
private UserDao userDao;

@Test
public void testGetUser() {
    // 模拟getUserById方法,当传入用户ID为1时,返回一个新的User对象
    when(userDao.getUserById(1)).thenReturn(new User(1, "John"));

    // 调用测试方法
    User user = userDao.getUserById(1);

    // 断言结果
    assertEquals("John", user.getName());
}

when(...).thenReturn(...):表示当调用 userDao.getUserById(1) 时,返回 new User(1, "John")

2. 使用 doReturn(...).when(...) 来模拟方法返回值

when(...).thenReturn(...) 类似,但适用于某些特殊情况,例如需要避免实际调用真实方法(尤其是部分模拟 @Spy 时),或者处理 void 方法的情况。

示例:使用 doReturn 避免部分模拟的真实方法被调用。

@Spy
private UserDao userDao;

@Test
public void testSaveUser() {
    // 避免调用真实的saveUser方法
    doReturn(1).when(userDao).saveUser(any(User.class));

    // 调用saveUser方法
    userDao.saveUser(new User(2, "Doe"));

    // 验证saveUser确实被调用过一次
    verify(userDao, times(1)).saveUser(any(User.class));
}

doReturn(...).when(...):适用于你不希望真实调用某个方法的情况。

when.thenReturn和doReturn.when的区别
1. when(...).thenReturn(...)

这是 Mockito 的标准使用方式,用于定义当某个方法被调用时返回指定的值。它适用于绝大多数场景,尤其是当你使用完全模拟对象(即 @Mock)时。

这种写法会去实际执行代码,然后返回指定值

示例

when(userDao.getUserById(2)).thenReturn(null);
  • 工作原理:Mockito 在内部是通过调用 userDao.getUserById(2) 方法,并在该方法执行后记录这个调用,然后当方法被再次调用时,返回 null
  • 调用时机when(...).thenReturn(...) 实际上会首先调用目标方法 getUserById(2),然后再返回指定的结果。如果目标方法是有副作用的(比如修改某些状态),它会先执行副作用,再进行模拟。
2. doReturn(...).when(...)

doReturn(...).when(...) 是 Mockito 中的另一种方法,主要用于避免方法调用本身带来的副作用,尤其是在 部分模拟@Spy)的场景中非常有用。

这种写法不会执行代码,直接返回指定值。

示例

doReturn(null).when(userDao).getUserById(2);
  • 工作原理doReturn(null) 先定义了模拟的返回值,然后使用 when(userDao) 来指定在 getUserById(2) 方法被调用时返回 null,而不会先调用 getUserById(2) 方法的真实实现。
  • 调用时机doReturn(...).when(...) 不会实际调用目标方法,因此不会触发任何真实方法的执行。如果目标方法有副作用或复杂的逻辑,使用 doReturn(...) 可以避免这些问题。
什么时候使用 doReturn(...).when(...)
  1. 处理 void 方法when(...).thenReturn(...) 不能用于模拟 void 方法,因为 void 方法没有返回值。这时你需要使用 doReturn()doThrow() 来模拟 void 方法的行为。

    示例

    doNothing().when(mockObject).someVoidMethod();
    
  2. 部分模拟(@Spy)的场景:当你使用部分模拟(@Spy)时,when(...).thenReturn(...) 实际上会调用真实方法。如果你不希望调用真实方法(比如该方法会改变对象状态或有副作用),可以使用 doReturn(...) 来避免真实方法的调用。

    示例

    @Spy
    private UserDao userDao;
    
    // 避免真实调用
    doReturn(null).when(userDao).getUserById(2);
    

    在这种情况下,when(userDao.getUserById(2)).thenReturn(null) 会实际调用 getUserById(2),但使用 doReturn(null) 则不会调用真实方法。

  3. 方法抛出异常的场景:某些情况下,方法在实际调用时会抛出异常。如果你不希望方法抛出异常(例如,你只关心返回结果的模拟),使用 doReturn(...) 可以避免直接调用导致的异常。

    示例

    doReturn(null).when(userDao).getUserById(2);
    

    如果 userDao.getUserById(2) 的真实方法抛出了异常,而你希望避免这种情况,则使用 doReturn(...) 可以跳过真实方法调用。

when(...).thenReturn(...) 的局限性

会实际调用方法:如果目标方法会触发某些副作用(例如修改数据或引发异常),when(...).thenReturn(...) 会首先调用该方法,然后记录返回结果,这有时不是你想要的行为,特别是在 @Spy 场景中。

示例:当使用部分模拟(@Spy)时,以下代码会先调用 getUserById(2),即真实方法会被调用:

when(userDao.getUserById(2)).thenReturn(null);

不能用于 void 方法:因为 when(...).thenReturn(...) 是针对有返回值的方法,如果你想模拟 void 方法(即不返回值的方法),则需要使用 doReturn()doThrow() 等方法。

3. 模拟方法抛出异常

可以使用 thenThrow(...)doThrow(...).when(...) 来模拟方法在被调用时抛出异常的场景,适合用于测试异常处理逻辑。

示例:模拟 saveUser 方法在调用时抛出异常。

@Mock
private UserDao userDao;

@Test
public void testSaveUserThrowsException() {
    // 模拟saveUser方法抛出异常
    doThrow(new RuntimeException("Database error")).when(userDao).saveUser(any(User.class));

    // 捕获异常
    assertThrows(RuntimeException.class, () -> {
        userDao.saveUser(new User(1, "John"));
    });

    // 验证saveUser方法确实被调用过一次
    verify(userDao, times(1)).saveUser(any(User.class));
}

doThrow(...).when(...)thenThrow(...):模拟方法抛出异常,用于测试异常处理逻辑。

4. 模拟 void 方法的行为

doNothing()doThrow() 是最常用的处理 void 方法的方式,前者模拟不执行任何操作,后者模拟抛出异常。

示例:模拟 void 方法 deleteUser 执行时不做任何事情。

@Mock
private UserDao userDao;

@Test
public void testDeleteUser() {
    // 模拟deleteUser方法执行时什么都不做
    doNothing().when(userDao).deleteUser(anyInt());

    // 调用测试方法
    userDao.deleteUser(1);

    // 验证deleteUser确实被调用过
    verify(userDao, times(1)).deleteUser(1);
}

doNothing().when(...):用于模拟 void 方法的行为,表示该方法什么都不做。

5. 使用 thenAnswer(...) 来模拟复杂行为

thenAnswer()允许你根据传入的参数、方法的调用上下文、甚至外部状态来动态地生成返回值或执行特定逻辑。相比于 thenReturn() 这种简单的返回值模拟方式,thenAnswer() 提供了更大的灵活性。

thenAnswer() 的主要特点:
  1. 基于输入参数动态响应:你可以根据方法的输入参数来生成不同的返回结果。
  2. 执行自定义逻辑:它允许你在模拟方法中执行特定的自定义逻辑,而不仅仅是返回一个固定值。
  3. 复杂行为模拟:适用于更复杂的业务场景,比如多个条件组合下的不同返回值,或者需要根据传入参数执行计算等。
thenAnswer() 使用方法

thenAnswer() 接受一个 Answer 接口的实现作为参数。Answer 接口定义了一个 answer(InvocationOnMock invocation) 方法,该方法会在模拟方法被调用时执行。你可以通过这个方法来访问方法的调用信息(包括传入的参数),并根据需要自定义返回结果或逻辑。

//Answer 接口定义
public interface Answer<T> {
    T answer(InvocationOnMock invocation) throws Throwable;
}
  • InvocationOnMock:提供了对当前调用的所有信息,包括参数、调用的 mock 对象等。
  • answer():在方法被调用时触发,用于自定义返回值或执行逻辑。

InvocationOnMock 接口提供了几个常用的方法,允许你访问模拟方法调用的详细信息:

  1. getMock():返回当前被调用的模拟对象。
  2. getMethod():返回被调用的 Method 对象。
  3. getArguments():返回方法的所有传递参数的数组。
  4. getArgument(int index):返回指定索引位置的单个参数。
  5. getArgument(int index, Class<T> clazz):返回指定索引的参数并强制转换为指定类型。
  6. getArgumentsCount():返回传递的参数数量。
  7. callRealMethod():调用被模拟方法的真实实现(常用于部分模拟 @Spy)。
示例:根据传入的参数动态返回不同结果

假设有一个 UserDaogetUserById 方法,你希望根据传入的用户 ID 来动态地返回不同的用户对象。

@Mock
private UserDao userDao;

@Test
public void testGetUserWithAnswer() {
    // 使用thenAnswer根据传入的参数返回不同的结果
    when(userDao.getUserById(anyInt())).thenAnswer(new Answer<User>() {
        @Override
        public User answer(InvocationOnMock invocation) throws Throwable {
            // 获取传入的参数(即用户ID)
            int userId = invocation.getArgument(0);
            // 根据用户ID返回不同的User对象
            return new User(userId, "User" + userId);
        }
    });

    // 调用测试方法
    User user1 = userDao.getUserById(1);
    User user2 = userDao.getUserById(2);

    // 验证返回结果
    assertEquals("User1", user1.getName());
    assertEquals("User2", user2.getName());
}

解释

  1. thenAnswer(new Answer<User>() {...}):在 getUserById 方法被调用时,触发 Answer 接口的 answer() 方法来生成返回值。
  2. invocation.getArgument(0):获取方法调用时的第一个参数,这里是传入的 userId
  3. 动态生成返回结果:根据传入的 userId,返回不同的 User 对象。
示例:抛出异常的场景

假设你想根据方法的传入参数决定是否抛出异常,可以使用 thenAnswer() 来实现。

@Mock
private UserDao userDao;

@Test
public void testGetUserThrowsException() {
    // 使用thenAnswer来模拟不同的异常抛出条件
    when(userDao.getUserById(anyInt())).thenAnswer(new Answer<User>() {
        @Override
        public User answer(InvocationOnMock invocation) throws Throwable {
            int userId = invocation.getArgument(0);
            if (userId < 0) {
                throw new IllegalArgumentException("User ID cannot be negative");
            }
            return new User(userId, "User" + userId);
        }
    });

    // 验证抛出异常
    assertThrows(IllegalArgumentException.class, () -> {
        userDao.getUserById(-1);
    });

    // 正常调用不抛异常
    User user = userDao.getUserById(1);
    assertEquals("User1", user.getName());
}

解释

  1. 根据传入参数抛出异常:当传入的 userId 小于 0 时,抛出 IllegalArgumentException,否则正常返回用户对象。
  2. 测试不同情况:我们通过 assertThrows 来验证方法在传入非法参数时抛出了预期的异常。
复杂场景:模拟调用次数或外部状态的变化

有时,你可能需要根据方法被调用的次数或外部状态的变化来决定返回值或执行逻辑。thenAnswer() 可以处理这些复杂场景。

示例:根据调用次数返回不同的结果

@Mock
private UserDao userDao;

@Test
public void testGetUserWithMultipleCalls() {
    // 使用thenAnswer来根据调用次数返回不同的结果
    when(userDao.getUserById(anyInt())).thenAnswer(new Answer<User>() {
        private int callCount = 0;  // 记录调用次数

        @Override
        public User answer(InvocationOnMock invocation) throws Throwable {
            callCount++;
            if (callCount == 1) {
                return new User(1, "First Call User");
            } else {
                return new User(1, "Subsequent Call User");
            }
        }
    });

    // 第一次调用返回"First Call User"
    User user1 = userDao.getUserById(1);
    assertEquals("First Call User", user1.getName());

    // 第二次及之后的调用返回"Subsequent Call User"
    User user2 = userDao.getUserById(1);
    assertEquals("Subsequent Call User", user2.getName());
}

解释

  1. callCount:通过一个计数器 callCount 来记录方法的调用次数。
  2. 根据调用次数返回不同的结果:第一次调用返回一个结果,后续调用返回不同的结果。
thenAnswer() 的优势
  • 灵活性高thenAnswer() 允许你在方法被调用时基于参数或其他条件生成返回值或抛出异常,比简单的 thenReturn() 更加灵活。
  • 动态行为模拟:可以根据方法的参数、调用次数、甚至外部状态来决定模拟的行为,适用于复杂的测试场景。
  • 测试特定逻辑:它可以帮助你测试依赖复杂逻辑的代码片段,尤其是在简单的 thenReturn()thenThrow() 无法满足需求时。

3. 执行测试代码并断言

编写测试代码,调用被测类中的方法。由于被测类的依赖已经被模拟对象替换,所以你可以专注于测试当前方法的逻辑,而不必担心真实依赖带来的副作用。在执行完测试代码后,你可以通过 JUnit 的断言 来检查测试结果是否符合预期。常用的断言包括 assertEquals()assertTrue()assertNull() 等。

JUnit 常用的断言方法

在 JUnit 5 中,所有断言方法都位于 org.junit.jupiter.api.Assertions 类中。常见的断言包括:

  1. assertEquals(expected, actual):断言两个值是否相等。
  2. assertNotEquals(unexpected, actual):断言两个值是否不相等。
  3. assertTrue(condition):断言条件为 true
  4. assertFalse(condition):断言条件为 false
  5. assertNull(object):断言对象是否为 null
  6. assertNotNull(object):断言对象是否不为 null
  7. assertSame(expected, actual):断言两个对象引用是否指向同一个对象。
  8. assertNotSame(unexpected, actual):断言两个对象引用是否不指向同一个对象。
  9. assertThrows(expectedType, executable):断言执行代码时抛出特定类型的异常。
  10. assertTimeout(duration, executable):断言在指定的时间内执行完成。
1. assertEquals(expected, actual)

assertEquals() 用于验证两个值是否相等,通常用于比较基本类型或重写了 equals() 方法的对象。

示例:

@Test
public void testAssertEquals() {
    int expected = 42;
    int actual = 42;
    assertEquals(expected, actual);  // 断言通过,两个值相等
}

可以带一个消息参数,方便调试:

assertEquals(expected, actual, "The values should be equal.");
2. assertNotEquals(unexpected, actual)

assertNotEquals() 用于验证两个值是否不相等。

示例:

@Test
public void testAssertNotEquals() {
    String actual = "Hello";
    String unexpected = "Goodbye";
    assertNotEquals(unexpected, actual);  // 断言通过,两个值不相等
}
3. assertTrue(condition)

assertTrue() 用于验证条件是否为 true

示例:

@Test
public void testAssertTrue() {
    boolean condition = 5 > 3;
    assertTrue(condition);  // 断言通过,条件为 true
}

可以带自定义消息:

assertTrue(condition, "The condition should be true.");
4. assertFalse(condition)

assertFalse() 用于验证条件是否为 false

示例:

@Test
public void testAssertFalse() {
    boolean condition = 5 < 3;
    assertFalse(condition);  // 断言通过,条件为 false
}
5. assertNull(object)

assertNull() 用于验证对象是否为 null

示例:

@Test
public void testAssertNull() {
    String value = null;
    assertNull(value);  // 断言通过,value 为 null
}
6. assertNotNull(object)

assertNotNull() 用于验证对象是否不为 null

示例:

@Test
public void testAssertNotNull() {
    String value = "JUnit";
    assertNotNull(value);  // 断言通过,value 不为 null
}
7. assertSame(expected, actual)

assertSame() 用于验证两个对象是否指向同一个引用(即,比较两个对象的内存地址是否相同)。

示例:

@Test
public void testAssertSame() {
    String str1 = "JUnit";
    String str2 = str1;  // 两个引用指向同一个对象
    assertSame(str1, str2);  // 断言通过
}
8. assertNotSame(unexpected, actual)

assertNotSame() 用于验证两个对象引用是否不相同。

示例:

@Test
public void testAssertNotSame() {
    String str1 = new String("JUnit");
    String str2 = new String("JUnit");
    assertNotSame(str1, str2);  // 断言通过,两个引用不相同
}
9. assertThrows(expectedType, executable)

assertThrows() 用于验证某段代码是否抛出了特定类型的异常。

示例:

@Test
public void testAssertThrows() {
    assertThrows(IllegalArgumentException.class, () -> {
        throw new IllegalArgumentException("Invalid argument");
    });
}

如果没有抛出异常或抛出了不同类型的异常,测试将失败。

10. assertTimeout(duration, executable)

assertTimeout() 用于验证某段代码是否在指定时间内完成。如果代码执行时间超过了指定的时间,测试将失败。

示例:

import java.time.Duration;

@Test
public void testAssertTimeout() {
    assertTimeout(Duration.ofSeconds(1), () -> {
        // 模拟耗时操作
        Thread.sleep(500);
    });
}
其他断言方法
1. assertArrayEquals(expectedArray, actualArray)

用于验证两个数组是否相等。

示例:

@Test
public void testAssertArrayEquals() {
    int[] expectedArray = {1, 2, 3};
    int[] actualArray = {1, 2, 3};
    assertArrayEquals(expectedArray, actualArray);  // 断言通过
}
2. assertIterableEquals(expected, actual)

用于验证两个 Iterable 对象是否相等。

示例:

@Test
public void testAssertIterableEquals() {
    List<String> expected = Arrays.asList("a", "b", "c");
    List<String> actual = Arrays.asList("a", "b", "c");
    assertIterableEquals(expected, actual);  // 断言通过
}
3. assertLinesMatch(expected, actual)

用于验证两个字符串列表的每一行是否匹配。常用于多行文本的比较。

示例:

@Test
public void testAssertLinesMatch() {
    List<String> expectedLines = Arrays.asList("line1", "line2");
    List<String> actualLines = Arrays.asList("line1", "line2");
    assertLinesMatch(expectedLines, actualLines);  // 断言通过
}
断言的灵活性

大多数 JUnit 断言方法都可以带上一个可选的第三个参数,表示失败时的自定义消息。自定义消息有助于在调试时快速找到问题。例如:

assertEquals(42, actualValue, "The actual value should be 42.");

当断言失败时,会输出 "The actual value should be 42." 这样的提示,帮助你快速定位问题。

4、验证行为:

验证行为的常用方法
  1. verify():验证某个模拟对象的方法是否被调用。
  2. verifyNoMoreInteractions():验证某个模拟对象的方法在指定的调用之外,没有其他额外的调用。
  3. verifyZeroInteractions() / verifyNoInteractions():验证某个模拟对象从未被调用。
  4. InOrder:验证多个方法调用的顺序。
  5. times():验证某个方法被调用的次数。
  6. never():验证某个方法从未被调用。
  7. atLeast()atMost():验证某个方法至少/至多被调用多少次。
1. 使用 verify() 验证方法调用

verify() 是 Mockito 验证调用关系的核心方法。它用于验证某个模拟对象的特定方法是否被调用。

示例:验证方法被调用

@Mock
private UserDao userDao;

@Test
public void testVerifyMethodCall() {
    // 模拟getUserById方法的行为
    when(userDao.getUserById(1)).thenReturn(new User(1, "John"));

    // 调用被测方法
    User user = userDao.getUserById(1);

    // 验证getUserById方法是否被调用过
    verify(userDao).getUserById(1);
}

在这个例子中,verify(userDao).getUserById(1) 验证了 getUserById(1) 是否被调用了一次。

2. 使用 times() 验证调用次数

有时,你不仅要验证方法是否被调用,还要验证它被调用了几次。Mockito 提供了 times() 方法来验证调用的次数。

示例:验证方法被调用的次数

@Mock
private UserDao userDao;

@Test
public void testVerifyCallTimes() {
    // 模拟调用行为
    when(userDao.getUserById(1)).thenReturn(new User(1, "John"));

    // 调用多次方法
    userDao.getUserById(1);
    userDao.getUserById(1);

    // 验证getUserById方法被调用了2次
    verify(userDao, times(2)).getUserById(1);
}

times(2):验证 getUserById(1) 被调用了两次。

3. 使用 never() 验证方法从未被调用

如果你需要验证某个方法从未被调用过,可以使用 never()

示例:验证方法从未被调用

@Mock
private UserDao userDao;

@Test
public void testVerifyNeverCalled() {
    // 调用其他方法
    userDao.saveUser(new User(1, "John"));

    // 验证getUserById方法从未被调用
    verify(userDao, never()).getUserById(1);
}

never():验证 getUserById(1) 从未被调用过。

4. 使用 verifyNoMoreInteractions() 验证没有其他方法调用

verifyNoMoreInteractions() 用于确保在验证指定的方法调用之后,没有其他不必要的调用。

示例:验证没有额外的调用

@Mock
private UserDao userDao;

@Test
public void testVerifyNoMoreInteractions() {
    // 调用一个方法
    userDao.getUserById(1);

    // 验证getUserById方法被调用过
    verify(userDao).getUserById(1);

    // 验证没有其他多余的方法调用
    verifyNoMoreInteractions(userDao);
}

verifyNoMoreInteractions():确保除了 getUserById(1) 外,userDao 没有其他方法被调用。

5. 使用 verifyZeroInteractions()verifyNoInteractions() 验证没有任何交互

verifyZeroInteractions()(或 verifyNoInteractions())用于验证某个模拟对象的任何方法都没有被调用过。

示例:验证没有任何方法调用

@Mock
private UserDao userDao;

@Test
public void testVerifyZeroInteractions() {
    // 不调用任何方法

    // 验证userDao没有任何方法被调用
    verifyZeroInteractions(userDao);
    // 或者使用 verifyNoInteractions(userDao);
}

verifyZeroInteractions()verifyNoInteractions():验证 userDao 没有任何方法被调用。

6. 使用 InOrder 验证调用顺序

InOrder 用于验证方法的调用顺序。如果你有多个方法调用,并且需要确保它们按特定顺序调用,InOrder 非常有用。

示例:验证方法调用的顺序

@Mock
private UserDao userDao;

@Mock
private NotificationService notificationService;

@Test
public void testVerifyCallOrder() {
    // 调用多个方法
    userDao.saveUser(new User(1, "John"));
    notificationService.notifyUser(1);

    // 验证调用顺序
    InOrder inOrder = inOrder(userDao, notificationService);
    inOrder.verify(userDao).saveUser(any(User.class));  // 验证saveUser先被调用
    inOrder.verify(notificationService).notifyUser(1);  // 然后notifyUser被调用
}

inOrder():验证 saveUsernotifyUser 方法是否按照指定的顺序被调用。

7. 使用 atLeast()atMost() 验证调用的最小/最大次数

如果你需要验证某个方法被调用的次数在某个范围内,Mockito 提供了 atLeast()atMost() 来验证方法的最少和最多调用次数。

示例:验证调用的最小次数

@Mock
private UserDao userDao;

@Test
public void testVerifyAtLeast() {
    // 调用方法多次
    userDao.getUserById(1);
    userDao.getUserById(1);
    userDao.getUserById(1);

    // 验证getUserById方法至少被调用2次
    verify(userDao, atLeast(2)).getUserById(1);
}

atLeast(2):验证 getUserById(1) 至少被调用了 2 次。

示例:验证调用的最大次数

@Mock
private UserDao userDao;

@Test
public void testVerifyAtMost() {
    // 调用方法多次
    userDao.getUserById(1);
    userDao.getUserById(1);

    // 验证getUserById方法最多被调用2次
    verify(userDao, atMost(2)).getUserById(1);
}

atMost(2):验证 getUserById(1) 最多被调用了 2 次。

具体演示:

class UserServiceTest {
	//1、准备测试环境
    @Mock
    WebServiceClient webServiceClient;
    @Mock
    UserDao userDao;
    @InjectMocks
    UserService userService;
	//初始化Mock
    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
    }
	//测试用例,从本地获取用户数据
    @Test
    void getLocalDBUser() {
        User MockUser = User.builder().id(1).name("张三").build();
        //2、定义模拟行为
        when(userDao.getUserById(1)).thenReturn(MockUser);
		//3、业务测试代码
        User ReturnedUser = userService.getUser(1);
		//断言结果
        assertEquals("张三",ReturnedUser.getName());
		//4、验证行为
        verify(userDao,times(1)).getUserById(1);
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

QT 中 sqlite 数据库使用

一、前提 --pro文件添加sql模块QT core gui sql二、使用 说明 --用于与数据库建立连接QSqlDatabase--执行各种sql语句QSqlQuery--提供数据库特定的错误信息QSqlError查看qt支持的驱动 QStringList list QSqlDatabase::drivers();qDebug()<<list;连接 sqlite3 数据库 …

微信小程序配置less并使用

1.在VScode中下载Less插件 2.在微信小程序中依次点击如下按钮 选择 从已解压的扩展文件夹安装… 3.选中刚在vscode中下载安装的插件文件 如果没有修改过插件的安装目录&#xff0c;一般是在c盘下C:\用户\用户名.vscode\extensions\mrcrowl.easy-less-2.0.2 我的路径是&#xf…

ClouderaManager 集群搭建

前提&#xff1a;服务器之前做过域名映射、免密登录 ClouderaManager 集群 1. 组件分布规划 服务器服务器h1zk、hdfs(dn)、yarn(nm)、spark、kafka、flumeh2hdfs(nn-standy)、yarn(rm-active)、sparkh3hdfs(nn-active)、yarn(rm-standy)、hive、sparkh4zk、hdfs(dn)、yarn(n…

OpenHarmony-3.HDF框架(2)

OpenHarmony HDF 平台驱动 1.平台驱动概述 系统平台驱动框架是系统驱动框架的重要组成部分&#xff0c;它基于HDF驱动框架、操作系统适配层(OSAL, operating system abstraction layer)以及驱动配置管理机制&#xff0c;为各类平台设备驱动的实现提供标准模型。 系统平台驱动(…

Guiding a Diffusion Model with a Bad Version of Itself

Guiding a Diffusion Model with a Bad Version of Itself Abstract1. Introduction2. Background3. Why does CFG improve image quality?Score matching leads to outliers.CFG 消除异常值Discussion 4 Our method Abstract 在图像生成扩散模型中&#xff0c;主要关注的轴心…

Tomcat,javaweb, servlet , springBoot

在server.xml里配置服务器 <scope>provided</scope>打包的时候&#xff0c;这个jar包不会被打进去&#xff0c;因为tomcat已将封装了这个jar包&#xff0c;没必要要这个

D614 PHP+MYSQL +失物招领系统网站的设计与现 源代码 配置 文档

失物招领系统 1.摘要2. 系统开发的背景和意义3.功能结构图4.界面展示5.源码获取 1.摘要 随着互联网的迅速发展&#xff0c;人们的生产生活方式逐渐发生改变&#xff0c;传统的失物招领也可以通过网络处理。本网站是基PHP技术的一款综合性较强的西南民族大学PHP失物招领系统。 …

单总线one-Wire

单总线one-Wire 概述 One-Wire总线是DALLAS公司研制开发的一种协议 特点&#xff1a; 它是由一个总线主节点&#xff0c;一个或多个从节点组成系统&#xff0c;通过一根信号线对从芯片进行数据的读取 每一个符合One-Wire协议的从芯片都有一个唯一的地址&#xff0c;包括48位的…

【llm】——香橙派AIPRO跑qwen2.5-0.5B

qwen2在Ascend310B4上的评测 代码/模型&#xff1a;https://github.com/chenjun2hao/qwen-ascend-llm 依赖 香橙派AIPRO&#xff1a;http://www.orangepi.cn/html/hardWare/computerAndMicrocontrollers/details/Orange-Pi-AIpro.htmlCANN8.0&#xff08;自己手动安装的&#…

Android 单元测试断言校验方法 org.junit.Assert

判断布尔值 assertTrue assertFalse 判断对象非空 assertNull(object); 案例&#xff1a; PersistableBundle result Util.getCarrierConfig(mockContext, subId);assertNull(result); 判断是否相等 assertEquals("mocked_string", result.toString()); package or…

Flink 中双流 Join 的深度解析与实战

目录 一、Join 算子 一&#xff09;语义与特性 二&#xff09;通用用法 三&#xff09;不同窗口类型表现 滚动窗口 Join 滑动窗口 Join 二、CoGroup 算子 一&#xff09;功能特点 二&#xff09;通用用法与连接类型实现 内连接&#xff08;InnerJoin&#xff09; 左…

OpenStack-Glance组件

Glance Glance使用磁盘格式和容器格式基础配置镜像转换 Glance 是 OpenStack 的镜像服务&#xff0c;负责存储、发现和管理虚拟机镜像。它允许用户创建和共享镜像&#xff0c;用于启动虚拟机实例。 Glance 的主要功能 &#xff08;1&#xff09;虚拟机镜像的管理 支持镜像的上…

基于神经网络的弹弹堂类游戏弹道快速预测

目录 一、 目的... 1 1.1 输入与输出.... 1 1.2 隐网络架构设计.... 1 1.3 激活函数与损失函数.... 1 二、 训练... 2 2.1 数据加载与预处理.... 2 2.2 训练过程.... 2 2.3 训练参数与设置.... 2 三、 测试与分析... 2 3.1 性能对比.... 2 3.2 训练过程差异.... 3 四、…

Linux入门攻坚——40、Linux集群系统入门-lvs(1)

Cluster&#xff0c;集群&#xff0c;为了解决某个特定问题将多台计算机组合起来形成的单个系统。 这个单个集群系统可以扩展&#xff0c;系统扩展的方式&#xff1a;scale up&#xff0c;向上扩展&#xff0c;更换更好的主机&#xff1b;scale out&#xff0c;向外扩展&…

威联通-001 手机相册备份

文章目录 前言1.Qfile Pro2.Qsync Pro总结 前言 威联通有两种数据备份手段&#xff1a;1.Qfile Pro和2.Qsync Pro&#xff0c;实践使用中存在一些区别&#xff0c;针对不同备份环境选择是不同。 1.Qfile Pro 用来备份制定目录内容的。 2.Qsync Pro 主要用来查看和操作文…

Docker单机网络:解锁本地开发环境的无限潜能

作者简介&#xff1a;我是团团儿&#xff0c;是一名专注于云计算领域的专业创作者&#xff0c;感谢大家的关注 座右铭&#xff1a; 云端筑梦&#xff0c;数据为翼&#xff0c;探索无限可能&#xff0c;引领云计算新纪元 个人主页&#xff1a;团儿.-CSDN博客 目录 前言&#…

【Linux操作系统】多线程控制(创建,等待,终止、分离)

目录 一、线程与轻量级进程的关系二、进程创建1.线程创建线程创建函数&#xff08;pthread&#xff09;查看和理解线程id主线程与其他线程之间的关系 三、线程等待&#xff08;回收&#xff09;四、线程退出线程退出情况线程退出方法 五、线程分离线程的优点线程的缺点 一、线程…

解决IDEA的easycode插件生成的mapper.xml文件字段之间逗号丢失

问题 easycode插件生成的mapper.xml文件字段之间逗号丢失&#xff0c;如图 解决办法 将easycode(在settings里面的othersettings)设置里面的Template的mapper.xml.vm和Global Config的mybatisSupport.vm的所有$velocityHasNext换成$foreach.hasNext Template的mapper.xml.vm(…

Android 实现中英文切换

在开发海外项目的时候&#xff0c;需要实现app内部的中英文切换功能&#xff0c;所有的英文都是内置的&#xff0c;整体思路为&#xff1a; 创建一个sp对象&#xff0c;存储当前系统的语言类型&#xff0c;然后在BaseActivity中对语言进行判断&#xff1b; //公共Activitypubl…

11月 | Apache DolphinScheduler月度进展总结

各位热爱 Apache DolphinScheduler 的小伙伴们&#xff0c;社区10月份月报更新啦&#xff01;这里将记录 DolphinScheduler 社区每月的重要更新&#xff0c;欢迎关注&#xff01; 月度Merge之星 感谢以下小伙伴11月份为 Apache DolphinScheduler 所做的精彩贡献&#xff08;排…