Junit 5 使用 mockConstruction().withSetting().useConstructor() 而不是 PowerMock.whenNew().withArguments()

Junit 5 use mockConstruction().withSetting().useConstructor() instead of PowerMock.whenNew().withArguments()

现在我想使用 Junit 5 + Mockito 4.x 版本 + Mockito-inline 4.x 版本而不是 Junit 4 + PowerMock 2.0.9

因为 Junit 5 不支持 PowerMock 也 Mockito-inline 可以模拟静态,看起来它不再需要 PowerMock。

但是当我使用 Mockito 模拟静态时,我想使用与 Powermock.whenNew(xxx.class).withArgument(1,2,3,4).thanReturn(someThing) 相同的效果。

这是我的代码的一部分,它可以工作。

    @Test
    void get_report_page() {
        ReportPageRequest reportPageRequest = prepare_request();
        prepare_reportPage(context, 9999L, pageable);

        when(reportConverter.toReportSpecification(user, reportPageRequest)).thenReturn(reportSpecification);
        when(PageRequest.of(1, 100)).thenReturn(pageRequest);
        when(reportRepository.findAll(reportSpecification, pageRequest)).thenReturn(reportPage);
        when(reportConverter.toReportPageResponse(context)).thenReturn(reportPageResponses);
        pageMockedConstruction = Mockito.mockConstruction(PageImpl.class,
                withSettings().useConstructor(reportPageResponses, pageable, 9999L), (mock, context) -> {
                    when(mock.getTotalElements()).thenReturn(123456L);
                    when(mock.getTotalPages()).thenReturn(1);
                    when(mock.getContent()).thenReturn(reportPageResponses);
                });

        Page<ReportPageResponse> actual = sut.getReportPage(user, reportPageRequest);

        assertThat(actual.getTotalElements()).isEqualTo(123456L);
        assertThat(actual.getTotalPages()).isEqualTo(1);
        assertThat(actual.getContent()).isEqualTo(reportPageResponses);
    }
}

我的问题是我只能验证模拟静态对象的行为,但无法验证结果,这是我的尝试

pageMockedConstruction = Mockito.mockConstruction(PageImpl.class,
                withSettings().useConstructor(reportPageResponses, pageable, 9999L), (mock, context) -> {
                    when(mock.getTotalElements()).thenReturn(123456L);
                    when(mock.getTotalPages()).thenReturn(1);
                    when(mock.getContent()).thenReturn(reportPageResponses);
                });

        // I thought here will be the same mock object
        // when expected and actual will throught the Mockito.mockConstruction, but actually generate the different object
        PageImpl<ReportPageResponse> expected = new PageImpl<>(this.reportPageResponses, pageable, 9999L);
        Page<ReportPageResponse> actual = sut.getReportPage(user, reportPageRequest);

        // Here will be wrong, because actual and expected has different hashCode
        Assertions.assertThat(actual).isEqualTo(expected);

我研究了很多文章,但找不到答案。

有人遇到过同样的问题吗?

Powermock.whenNewMockito.mockConstruction 的主要区别是Mokito 在调用构造函数时每次实例化新对象时都会创建一个新的mock。但是 Powermock.whenNew 可以配置为 return 一个 mock 总是用于构建多个对象。
根据documentation

Represents a mock of any object construction of the represented type. Within the scope of the mocked construction, the invocation of any interceptor will generate a mock which will be prepared as specified when generating this scope. The mock can also be received via this instance.

您可以使用 MockedConstruction<T>.constructed() 在上下文中获取所有生成的模拟。它们可以用于验证。

检查行为的测试示例:

public class A {
    private final String test;

    public A(String test) {
        this.test = test;
    }

    public String check() {
        return "checked " + this.test;
    }
}

public class TestService {
    public String purchaseProduct(String param) {
        A a = new A(param);
        return a.check();
    }
}

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockedConstruction;
import org.mockito.Mockito;

import static org.mockito.Mockito.*;

public class ConstructorMockTest {
    private MockedConstruction<A> mockAController;

    @BeforeEach
    public void beginTest() {
        //create mock controller for all constructors of the given class
        mockAController = Mockito.mockConstruction(A.class,
                (mock, context) -> {
                    //implement initializer for mock. Set return value for object A mock methods
                    //this initializer will be called each time during mock creation 
                    when(mock.check()).thenReturn(" Constructor Mock A ");
                });
    }

    @Test
    public void test() {
        //each instantiation of class A will return new mock, which initialized by initializer from beginTest method
        //new mock will be stored to mockAController.constructed() collection of mocks
        A aObject = new A("test");
        //ensure that method check() returns mocked value
        Assertions.assertEquals(aObject.check(), " Constructor Mock A ");
        //get just created mock for class A from controller. It will be first element of mockAController.constructed() collection
        A aMock = mockAController.constructed().get(0);
        //ensure that we get correct mock from mock controller, that it is equal from new created object
        Assertions.assertEquals(aMock, aObject);
        //verify that check method was executed on Mock
        verify(aMock, times(1)).check();

        //create new A object, new mock created and stored to mockAController.constructed()
        A aObject2 = new A("test");
        //ensure that method check() returns mocked value
        Assertions.assertEquals(aObject2.check(), " Constructor Mock A ");
        //get just created mock for class A from controller, it will be second object from constructed collection
        A aMock2 = mockAController.constructed().get(1);
        //ensure that we get correct mock from mock controller, that it is equal from just created A object
        Assertions.assertEquals(aObject2, aMock2);
        //verify that check method was executed on Mock
        verify(aMock2, times(1)).check();

        //Example of testing service which creates A object
        TestService service = new TestService();
        String serviceResult = service.purchaseProduct("test");
        //ensure that service returned value  from A mock
        Assertions.assertEquals(serviceResult, " Constructor Mock A ");
        //get just created mock for class A from controller, it will be third object from constructed collection
        A aMock3 = mockAController.constructed().get(2);
        //verify that check method was executed on Mock
        verify(aMock3, times(1)).check();
    }

    @AfterEach
    public void endTest() {
        mockAController.close();
    }
}