在学习Mockito 如何集成 Junit5 之前,先来学习下 Mockito 基础的verify功能。
本篇博客代码的Maven依赖如下,源码地址
org.springframework spring-web 6.0.2 org.springframework spring-core 6.0.2 org.springframework spring-context 6.0.2 org.apache.commons commons-lang3 3.12.0 org.mockito mockito-junit-jupiter 4.8.1 test org.projectlombok lombok 1.18.22 compile com.google.guava guava 31.1-jre org.mockito mockito-core 4.8.1 test org.assertj assertj-core 3.22.0 test
Mockito Verify 方法经常被用于检查校验对象是否发生特定行为 - 即方法. 开发者可在测试方法代码的末尾使用verify方法, 确定是否调用了指定的方法。Mockito Verify主要从以下几个方面对调用行为进行判断
接下来详细学习下 Mockito Verify 相关的API
@Testvoid test1(){List list = mock(ArrayList.class);list.size();// 验证是否调用size 方法verify(list).size();}
验证没有调用其他方法
@Testvoid test2(){List list = mock(ArrayList.class);// 验证list 对象没发生额外调用verifyNoMoreInteractions(list);}
验证从没调用过指定方法
@Testvoid test3(){List list = mock(ArrayList.class);list.size();//从未调用过clear 方法verify(list,never()).clear();}
验证仅调用一次
@Testvoid test0(){List list = mock(ArrayList.class);list.size();// times(1), only(),atLeast(1) 都能实现一次调用的验证//verify(list,times(1)).size();//verify(list,only()).size();verify(list,atLeast(1)).size();}
验证至少调用次数
@Testvoid test0(){List list = mock(ArrayList.class);list.size();// 最少一次调用验证verify(list,atLeastOnce()).size();}
验证最多调用次数
@Testvoid test0(){List list = mock(ArrayList.class);list.size();//最多一次verify(list,atMostOnce()).size();// 最多10次verify(list,atMost(10)).size();}
@Testvoid test3(){List list = mock(ArrayList.class);list.size();list.add("hello");list.clear();InOrder inOrder = Mockito.inOrder(list);inOrder.verify(list).size();inOrder.verify(list).add(anyString());inOrder.verify(list).clear();}
验证指定参数
@Testvoid test6(){List list = mock(ArrayList.class);list.add("hello");verify(list).add("hello");// 以下两种 验证参数类型verify(list).add(anyString());verify(list).add(any(String.class));}
验证捕获参数
@Testvoid test7(){List list = mock(ArrayList.class);list.addAll(Lists. newArrayList("someElement"));ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class);verify(list).addAll(captor.capture());// 捕获参数 并验证是否包含特定值final List value = captor.getValue();assertThat(value).contains("someElement");}
有了之前的基础后,接下来来学习下Mockito 如何集成 Junit5,了解Junit5如何对Mockito进行扩展。大致的流程:
假设现在有一个项目,团队需要基于SpringBoot MVC的架构模式进行开发。由于业务足够复杂,分工细致。你被要求开发 service层的业务逻辑,Repository数据操作层由其他团队负责,双方根据约定好的接口进行协同开发。
由于你的专业技术能力过硬,开发效率较高,因此已经完成了开发工作。但是其他团队Repository没有完成,只提供了对应的接口。现在您需要对整理流程进行集成测试,此时Mock排上用场。
User 实体类
@Data
@ToString
@NoArgsConstructor
public class User {private Integer id;private String name;private int age;public User(String name,int age){this.name = name;this.age = age;}
}
service接口
import com.andy.spring.junit5.mockito.User;public interface UserService {User register(User user);
}
service实现类
import com.andy.spring.junit5.mockito.User;
import com.andy.spring.junit5.mockito.repository.UserRepository;
import com.andy.spring.junit5.mockito.service.UserService;public class DefaultUserService implements UserService {private UserRepository userRepository;public DefaultUserService(UserRepository userRepository) {this.userRepository = userRepository;}@Overridepublic User register(User user) {validate(user);User insertedUser = userRepository.insert(user);return insertedUser;}private void validate(User user) {if(user.getName() == null) {throw new RuntimeException("用户名称不能为空");}if(userRepository.isUsernameAlreadyExists(user.getName())) {throw new RuntimeException("用户名称已存在");}}
}
Repository 接口定义
import com.andy.spring.junit5.mockito.User;public interface UserRepository {User insert(User user);boolean isUsernameAlreadyExists(String userName);}
@ExtendWith(MockitoExtension.class)
public class UserServiceTest {private UserService userService;@Mockprivate UserRepository userRepository;private User user;//每次运行测试方法都进行初始化@BeforeEachvoid init(){userService = new DefaultUserService(userRepository);//输入任意字符串 参数校验都返回falselenient().when(userRepository.isUsernameAlreadyExists(any(String.class))).thenReturn(false);}@Testvoid test1(){user = new User("kobe",40);when(userRepository.insert(any(User.class))).then(new Answer() {int sequence = 1;@Overridepublic User answer(InvocationOnMock invocation) throws Throwable {//模拟插入db后,对ID主键进行赋值User user = (User) invocation.getArgument(0);user.setId(sequence++);return user;}});userService = new DefaultUserService(userRepository);// WhenUser insertedUser = userService.register(user);System.out.println("insertedUser : " + insertedUser);// Then 验证是否调用 insert 方法 参数为userverify(userRepository).insert(user);assertNotNull(user.getId());}
}
以上代码很容易理解,值得注意的是需要在class类上加上**@ExtendWith(MockitoExtension.class)**注解。测试截图