如何使用Mockito模拟Pageable对象?

时间:2019-07-15 18:59:53

标签: java junit pagination spring-data-jpa mockito

我正在开发Spring Boot + Data JPA + Mockito示例。我已经为Pagination存储库方法开发了测试用例,但是它给了我下面的错误。

org.mockito.exceptions.misusing.InvalidUseOfMatchersException: 
Misplaced or misused argument matcher detected here:

-> at com.xxxx.EmployeeServiceTest.test_findAllEmployeesPaginated(EmployeeServiceTest.java:152)
-> at com.xxxx.EmployeeServiceTest.test_findAllEmployeesPaginated(EmployeeServiceTest.java:152)
-> at com.xxxx.EmployeeServiceTest.test_findAllEmployeesPaginated(EmployeeServiceTest.java:152)
-> at com.xxxx.EmployeeServiceTest.test_findAllEmployeesPaginated(EmployeeServiceTest.java:152)

You cannot use argument matchers outside of verification or stubbing.
Examples of correct usage of argument matchers:
    when(mock.get(anyInt())).thenReturn(null);
    doThrow(new RuntimeException()).when(mock).someVoidMethod(anyObject());
    verify(mock).someMethod(contains("foo"))

This message may appear after an NullPointerException if the last matcher is returning an object 
like any() but the stubbed method signature expect a primitive argument, in this case,
use primitive alternatives.
    when(mock.get(any())); // bad use, will raise NPE
    when(mock.get(anyInt())); // correct usage use

Also, this error might show up because you use argument matchers with methods that cannot be mocked.
Following methods *cannot* be stubbed/verified: final/private/equals()/hashCode().
Mocking methods declared on non-public parent classes is not supported.

    at com.xxxx.EmployeeServiceTest.test_findAllEmployeesPaginated(EmployeeServiceTest.java:152)
    at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:68)
    at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:89)
    at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:97)
    at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:87)
    at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:50)
    at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:34)
    at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:44)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:538)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:760)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:460)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206)

控制器

@GetMapping()
public ResponseEntity<Page<Division>> getAllDivisionsPaginated(
    @RequestParam(required = false, value="page") Integer page, @RequestParam(required = false, value = "size") Integer size,
    @RequestParam(required = false) String sort,
    @RequestParam(required = false) String direction,
    Pageable pageable){

    Page<Division> divisions = divisionService.findAllDivisions(pageable, page, size, sort, direction);
    return new ResponseEntity<>(divisions, HttpStatus.OK);
}

方法:

public Page<Division> findAllEmployees(Pageable pageableReq, Integer page, Integer size, String sortBy, String direction) {
    Pageable pageable = Utils.sort(pageableReq, page, size, sortBy, direction);
    return employeeRepository.findByStatus(StatusEnum.A, pageable);
}

另一种方法:

public static Pageable sort(Pageable pageableReq, Integer page, Integer size, String sortBy, String direction) {
     Pageable pageable = null;
     if (page != null && size != null) {
         if (direction.equalsIgnoreCase("ASC"))
             pageable = PageRequest.of(page, size, Sort.by(sortBy).ascending());
         else if (direction.equalsIgnoreCase("DESC"))
             pageable = PageRequest.of(page, size, Sort.by(sortBy).descending());
     } else {
         pageable = pageableReq;
     }
    return pageable;
}

测试课程:

@RunWith(PowerMockRunner.class)
@PrepareForTest({StatusEnum.class})
public class EmployeeServiceTest {

    @Mock
    private Employee employeeMock;

    @InjectMocks
    private EmployeeServiceImpl employeeServiceImpl;

    @Mock
    private EmployeeRepository employeeRepositoryMock;

    @Mock
    private EmployeeDto employeeDtoMock;

    @Mock
    private StatusEnum statusEnum;

    @Mock
    private Exception ex;

    @Mock
    private Pageable pageableMock;

    @Mock
    private Utils utils;

    @Mock
    private Page<Employee> employeePage;

    @Before
    public void setup() {
        mockStatic(StatusEnum.class);       
    }

    @Test
    public void test_findAllEmployeesPaginated() {
        Pageable pageable = PageRequest.of(0, 8);
        when(Utils.sort(pageable, anyInt(), anyInt(), anyString(), anyString())).thenReturn(pageableMock);
        when(employeeRepositoryMock.findByStatus(StatusEnum.A, pageableMock)).thenReturn(employeePage);
    }   
}

2 个答案:

答案 0 :(得分:1)

  1. 纯Mockito无法模拟静态方法。 (您需要使用对象引用utils而不是类引用,但是您的代码表明该方法是静态的)
    如果您想在此处使用Powermock,则可能会丢失一个模拟状态(Utils.class),但是我不确定这是否可以与@Mock注释一起使用。

  2. 此外,您还必须更改sort方法的调用。您必须仅使用匹配器或仅使用实数值。您不能混合使用它们。

  3. 关于跑步者@RunWith(PowerMockRunner.class)。如果仅编写纯JUnit 4测试和Mocktio,则应考虑改用Mockito Runner。
    否则,您需要在设置方法中使用MockitoAnnotations.initMocks(this);来初始化Mockito。

(您没有使用PowerMock标记问题,但我认为那可能是不正确的。)

    @Before
    public void setup() throws Exception {
        MockitoAnnotations.initMocks(this);
        mockStatic(StatusEnum.class);       
    }

    @Test
    public void test_findAllEmployeesPaginated() {
        Pageable pageable = PageRequest.of(0, 8);

        mockStatic(Utils.class);
        when(Utils.sort(any(Pageable.class), anyInt(), anyInt(), anyString(), anyString())).thenReturn(pageableMock);
        when(employeeRepositoryMock.findByStatus(StatusEnum.A, pageableMock)).thenReturn(employeePage);
    }

答案 1 :(得分:0)

问题出在测试用例的模拟存根。您正在做mix of actual objects and mocked objects which is not a supported functionality in Mockito。您可以使用all real objectsall mocked objects

在您的测试用例中

when(Utils.sort(pageable, anyInt(), anyInt(), anyString(), anyString())).thenReturn(pageableMock);

pageable是实际的对象,其余的对象都是模拟实例。

将其替换为以下内容,以将所有模拟的实例用于您的模拟调用,如下所示:

when(Utils.sort(any(Pageable.class), anyInt(), anyInt(), anyString(), anyString())).thenReturn(pageableMock);