Mockito:如何使用 JPA 规范中的参数验证方法调用?
Mockito: How do I verify method call with arguments on JPA Specification?
我正在为 Controller 编写单元测试并模拟对其服务的所有调用。我将 Spring 与 JPA 存储库一起使用。
@RestController
@CrossOrigin
@RequestMapping("/somerequest")
public class MyController {
@Autowired
UserService userService;
....
@RequestMapping("/filter")
public List<UserDTO> getUsersByFilter(@RequestParam(value = "search") String search) {
UserSpecificationsBuilder builder = new UserSpecificationsBuilder();
Pattern pattern = Pattern.compile("(\w+?)(:|<|>)(\w+?),", Pattern.UNICODE_CHARACTER_CLASS);
Matcher matcher = pattern.matcher(search + ",");
while (matcher.find()) {
builder.with(matcher.group(1), SearchOperation.getSimpleOperation(matcher.group(2)), matcher.group(3));
}
Specification<User> spec = builder.build();
List<User> result = userService.getUserByFilter(spec);
List<UserDTO> userDtoList = new ArrayList<UserDTO>();
if (!result.isEmpty()) {
for (User a : result) {
userDtoList.add(convertToDTO(a));
}
}
return userDtoList;
}
....
}
我如何 (Mockito) 验证使用哪些参数调用方法 getUserByFilter?
我使用 ArgumentCaptor 来捕获提供给该方法的 Specification 对象,但我不知道如何从中获取搜索条件。
非常感谢您的帮助
public class UserSpecificationsBuilder {
private final List<SearchCriteria> params;
public UserSpecificationsBuilder() {
params = new ArrayList<SearchCriteria>();
}
public UserSpecificationsBuilder with(String key, SearchOperation operation, Object value) {
params.add(new SearchCriteria(key, operation, value));
return this;
}
public Specification<User> build() {
if (params.size() == 0) {
return null;
}
List<Specification> specs = params.stream().map(UserSpecification::new).collect(Collectors.toList());
Specification<User> result = specs.get(0);
for (int i = 1; i < params.size(); i++) {
result = Specification.where(result).and(specs.get(i));
}
return result;
}
}
public class UserSpecification implements Specification<User> {
/**
*
*/
private static final long serialVersionUID = 1L;
private SearchCriteria criteria;
public UserSpecification(SearchCriteria criteria) {
this.criteria = criteria;
}
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
switch (criteria.getOperation()) {
case LIKE:
return builder.like(root.get(criteria.getKey()), "%" + criteria.getValue().toString() + "%");
case GREATER_THAN:
return builder.greaterThan(root.get(criteria.getKey()), criteria.getValue().toString());
case LESS_THAN:
return builder.lessThan(root.get(criteria.getKey()), criteria.getValue().toString());
default:
return null;
}
}
@Override
public boolean equals(Object obj) {
UserSpecification source = (UserSpecification) obj;
if (source.criteria != null) {
return this.criteria.equals(source.criteria);
}
return false;
}
}
public class SearchCriteria {
private String key;
private SearchOperation operation;
private Object value;
public SearchCriteria(final String key, final SearchOperation operation, final Object value) {
super();
this.key = key;
this.operation = operation;
this.value = value;
}
@Override
public boolean equals(Object obj) {
SearchCriteria source = (SearchCriteria) obj;
return this.key.equals(source.getKey()) && this.value.toString().equals(source.getValue().toString())
&& this.operation.equals(source.getOperation());
}
// getters and setters
}
我的测试:
@ContextConfiguration(classes = Application.class)
@WebMvcTest(UserController.class)
@DisplayName("UserController - Test")
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
...
@Test
void testGetUsererByFilter() throws Exception {
ArgumentCaptor<Specification> argsCaptor = ArgumentCaptor.forClass(Specification.class);
String filterURL = "/filter?search=personalnummer:6517,ueberstd>5";
given(this.userService.getUserByFilter(any(Specification.class)))
.willReturn(new ArrayList<User>());
mockMvc.perform(get(requestURL + filterURL)).andExpect(status().isOk())
.andExpect(handler().handlerType(UserController.class))
.andExpect(handler().methodName("getUserByFilter"));
UserSpecificationsBuilder builder = new UserSpecificationsBuilder();
UserSpecification spec1 = new UserSpecification(new SearchCriteria("personalnummer", SearchOperation.LIKE, "6517"));
builder.with(spec1);
UserSpecification spec2 = new UserSpecification(new SearchCriteria("ueberstd", SearchOperation.GREATER_THAN, "5"));
builder.with(spec2);
Specification<User> spec = builder.build();
verify(this.userService, times(1)).getUserByFilter(spec);
// verify(this.UsernehmerService, times(1)).getUserByFilter(argsCaptor.capture());
// List<Specification> capturedArgs = argsCaptor.getAllValues();
// assertEquals(Specification.where(spec1).and(spec2), Specification.where(capturedArgs.get(0)));
}
}
和用户服务:
@Service
public class UserService {
@Autowired
UserRepository userRepository;
public List<User> getUserByFilter(Specification<User> spec) {
List<User> userList = userRepository.findAll(spec);
return userList;
}
}
问题是您正在尝试比较 Specification.where(spec1).and(spec2)
返回的 lamda
public interface Specification<T> extends Serializable {
default Specification<T> and(Specification<T> other) {
return Specifications.composed(this, other, AND);
}
}
public class Specifications<T> implements Specification<T>, Serializable {
static <T> Specification<T> composed(@Nullable Specification<T> lhs, @Nullable Specification<T> rhs, CompositionType compositionType) {
return (root, query, builder) -> {
Predicate otherPredicate = rhs == null ? null : rhs.toPredicate(root, query, builder);
Predicate thisPredicate = lhs == null ? null : lhs.toPredicate(root, query, builder);
return thisPredicate == null ? otherPredicate
: otherPredicate == null ? thisPredicate : compositionType.combine(builder, thisPredicate, otherPredicate);
};
}
}
这些 lambda 没有可比性。
我鼓励您将一些逻辑从控制器转移到服务:
在您的服务中公开一个接受 UserSpecification
对象列表的方法。您可以在此 class.
中控制 UserSpecification
并覆盖 equals
我正在为 Controller 编写单元测试并模拟对其服务的所有调用。我将 Spring 与 JPA 存储库一起使用。
@RestController
@CrossOrigin
@RequestMapping("/somerequest")
public class MyController {
@Autowired
UserService userService;
....
@RequestMapping("/filter")
public List<UserDTO> getUsersByFilter(@RequestParam(value = "search") String search) {
UserSpecificationsBuilder builder = new UserSpecificationsBuilder();
Pattern pattern = Pattern.compile("(\w+?)(:|<|>)(\w+?),", Pattern.UNICODE_CHARACTER_CLASS);
Matcher matcher = pattern.matcher(search + ",");
while (matcher.find()) {
builder.with(matcher.group(1), SearchOperation.getSimpleOperation(matcher.group(2)), matcher.group(3));
}
Specification<User> spec = builder.build();
List<User> result = userService.getUserByFilter(spec);
List<UserDTO> userDtoList = new ArrayList<UserDTO>();
if (!result.isEmpty()) {
for (User a : result) {
userDtoList.add(convertToDTO(a));
}
}
return userDtoList;
}
....
}
我如何 (Mockito) 验证使用哪些参数调用方法 getUserByFilter? 我使用 ArgumentCaptor 来捕获提供给该方法的 Specification 对象,但我不知道如何从中获取搜索条件。 非常感谢您的帮助
public class UserSpecificationsBuilder {
private final List<SearchCriteria> params;
public UserSpecificationsBuilder() {
params = new ArrayList<SearchCriteria>();
}
public UserSpecificationsBuilder with(String key, SearchOperation operation, Object value) {
params.add(new SearchCriteria(key, operation, value));
return this;
}
public Specification<User> build() {
if (params.size() == 0) {
return null;
}
List<Specification> specs = params.stream().map(UserSpecification::new).collect(Collectors.toList());
Specification<User> result = specs.get(0);
for (int i = 1; i < params.size(); i++) {
result = Specification.where(result).and(specs.get(i));
}
return result;
}
}
public class UserSpecification implements Specification<User> {
/**
*
*/
private static final long serialVersionUID = 1L;
private SearchCriteria criteria;
public UserSpecification(SearchCriteria criteria) {
this.criteria = criteria;
}
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
switch (criteria.getOperation()) {
case LIKE:
return builder.like(root.get(criteria.getKey()), "%" + criteria.getValue().toString() + "%");
case GREATER_THAN:
return builder.greaterThan(root.get(criteria.getKey()), criteria.getValue().toString());
case LESS_THAN:
return builder.lessThan(root.get(criteria.getKey()), criteria.getValue().toString());
default:
return null;
}
}
@Override
public boolean equals(Object obj) {
UserSpecification source = (UserSpecification) obj;
if (source.criteria != null) {
return this.criteria.equals(source.criteria);
}
return false;
}
}
public class SearchCriteria {
private String key;
private SearchOperation operation;
private Object value;
public SearchCriteria(final String key, final SearchOperation operation, final Object value) {
super();
this.key = key;
this.operation = operation;
this.value = value;
}
@Override
public boolean equals(Object obj) {
SearchCriteria source = (SearchCriteria) obj;
return this.key.equals(source.getKey()) && this.value.toString().equals(source.getValue().toString())
&& this.operation.equals(source.getOperation());
}
// getters and setters
}
我的测试:
@ContextConfiguration(classes = Application.class)
@WebMvcTest(UserController.class)
@DisplayName("UserController - Test")
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
...
@Test
void testGetUsererByFilter() throws Exception {
ArgumentCaptor<Specification> argsCaptor = ArgumentCaptor.forClass(Specification.class);
String filterURL = "/filter?search=personalnummer:6517,ueberstd>5";
given(this.userService.getUserByFilter(any(Specification.class)))
.willReturn(new ArrayList<User>());
mockMvc.perform(get(requestURL + filterURL)).andExpect(status().isOk())
.andExpect(handler().handlerType(UserController.class))
.andExpect(handler().methodName("getUserByFilter"));
UserSpecificationsBuilder builder = new UserSpecificationsBuilder();
UserSpecification spec1 = new UserSpecification(new SearchCriteria("personalnummer", SearchOperation.LIKE, "6517"));
builder.with(spec1);
UserSpecification spec2 = new UserSpecification(new SearchCriteria("ueberstd", SearchOperation.GREATER_THAN, "5"));
builder.with(spec2);
Specification<User> spec = builder.build();
verify(this.userService, times(1)).getUserByFilter(spec);
// verify(this.UsernehmerService, times(1)).getUserByFilter(argsCaptor.capture());
// List<Specification> capturedArgs = argsCaptor.getAllValues();
// assertEquals(Specification.where(spec1).and(spec2), Specification.where(capturedArgs.get(0)));
}
}
和用户服务:
@Service
public class UserService {
@Autowired
UserRepository userRepository;
public List<User> getUserByFilter(Specification<User> spec) {
List<User> userList = userRepository.findAll(spec);
return userList;
}
}
问题是您正在尝试比较 Specification.where(spec1).and(spec2)
public interface Specification<T> extends Serializable {
default Specification<T> and(Specification<T> other) {
return Specifications.composed(this, other, AND);
}
}
public class Specifications<T> implements Specification<T>, Serializable {
static <T> Specification<T> composed(@Nullable Specification<T> lhs, @Nullable Specification<T> rhs, CompositionType compositionType) {
return (root, query, builder) -> {
Predicate otherPredicate = rhs == null ? null : rhs.toPredicate(root, query, builder);
Predicate thisPredicate = lhs == null ? null : lhs.toPredicate(root, query, builder);
return thisPredicate == null ? otherPredicate
: otherPredicate == null ? thisPredicate : compositionType.combine(builder, thisPredicate, otherPredicate);
};
}
}
这些 lambda 没有可比性。
我鼓励您将一些逻辑从控制器转移到服务:
在您的服务中公开一个接受 UserSpecification
对象列表的方法。您可以在此 class.
UserSpecification
并覆盖 equals