Search code examples
javaspringjunitmockito

PowerMock whenNew Alternative


I don't want to use powermock anymore. Because junit5 started mocking static classes. So i am trying to get rid of powermock methods.

As you know, you can create an instance of a class with whenNew keyword.

Is there any alternative in Junit5 for whenNew?

Here is a part of my code:

                whenNew(PDFDocument.class).withNoArguments().thenReturn(pdfDocument);
                whenNew(PSConverter.class).withNoArguments().thenReturn(converter);
                doNothing().when(pdfDocument).load(ArgumentMatchers.any(ByteArrayInputStream.class));
                doAnswer(invocationOnMock -> {
                    ByteArrayOutputStream outputStream = invocationOnMock.getArgument(1);
                    outputStream.write(content);
                    return outputStream;
                }).when(converter).convert(ArgumentMatchers.any(), ArgumentMatchers.any(ByteArrayOutputStream.class));

Solution

  • Mocking object construction is available since Mockito 3.5.0 according to documentation.
    First of all you need add the mockito-inline instead of the mockito-core to your test dependencies.
    mockito-inline provides ability to mock static or final methods, constructors. Difference between mockito-core vs mockito-inline

            <dependency>
                <groupId>org.mockito</groupId>
                <artifactId>mockito-inline</artifactId>
                <version>${mockito.version}</version>
                <scope>test</scope>
            </dependency>
    

    Let's create a simple service for testing which instantiate objects.

    public class A {
        private final String test;
    
        public A(String test) {
            this.test = test;
        }
    
        public String check() {
            return "checked " + this.test;
        }
    }
    
    public class B {
        private String check = " B check ";
    
        public String check() {
            return check;
        }
    
    }
    
    public class TestService {
        public String purchaseProduct(String param) {
            A a = new A(param);
            B b = new B();
            return a.check() + b.check();
        }
    }
    

    Example of constructor mock with comments:

    import org.junit.jupiter.api.Assertions;
    import org.junit.jupiter.api.Test;
    import org.mockito.MockedConstruction;
    import org.mockito.Mockito;
    
    import static org.mockito.Mockito.when;
    
    public class ConstructorMockTest {
        @Test
        public void test_mocked_construction()  {
            try (
                 //create mock for object A
                 MockedConstruction<A> mockedA = Mockito.mockConstruction(A.class,
                    (mock, context) -> {
                        //set return value for object A mock methods
                        when(mock.check()).thenReturn(" Constructor Mock A ");
                 });
                 //create mock for object B
                 MockedConstruction<B> mockedB = Mockito.mockConstruction(B.class,
                         (mock, context) -> {
                             //set return value for object B mock methods
                             when(mock.check()).thenReturn(" Constructor Mock B ");
                 }))
            {
                // every A object creation is current try() scope returning a mock
                A aObject = new A("test");
                Assertions.assertEquals( aObject.check(), " Constructor Mock A ");
    
                // every B object creation is current try() scope returning a mock
                B bObject = new B();
                Assertions.assertEquals( bObject.check(), " Constructor Mock B ");
    
                //Example of testing service which creates A and B objects
                TestService service = new TestService();
                String serviceResult = service.purchaseProduct("test");
    
                Assertions.assertEquals(serviceResult, " Constructor Mock A  Constructor Mock B ");
            }
        }
    }
    

    For your classes example:

        @Test
        public void test() {
            byte[] content = new byte[] {1,1};
    
            try (
                    MockedConstruction<PDFDocument> mockedPDFDocument = Mockito.mockConstruction(PDFDocument.class,
                            (mock, context) -> {
                                doNothing().when(mock).load(ArgumentMatchers.any(ByteArrayInputStream.class));
                            });
    
                    MockedConstruction<PSConverter> mockedPSConverter = Mockito.mockConstruction(PSConverter.class,
                            (mock, context) -> {
                                doAnswer(invocationOnMock -> {
                                    ByteArrayOutputStream outputStream = invocationOnMock.getArgument(1);
                                    outputStream.write(content);
                                    return outputStream;
                                }).when(mock).convert(ArgumentMatchers.any(), ArgumentMatchers.any(ByteArrayOutputStream.class));
                            }))
            {
                //call services which instantiates PDFDocument and PSConverter
                PDFDocument pdfDocument = new PDFDocument();
                PSConverter psConverter = new PSConverter();
    
                Assertions.assertTrue(org.mockito.internal.util.MockUtil.isMock(pdfDocument));
                Assertions.assertTrue(org.mockito.internal.util.MockUtil.isMock(psConverter));
            }
        }