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