Search code examples
javaunit-testingmockitojunit4powermockito

Mockito is unable to create instance using @InjectMocks because of Static class in constructor


I have to unit test in some code(Not written by me), but I am stuck at one place. I am using Powermock and mockito. So the issue is @InjectMocks call default constructor before even testing a method, inside the default constructor, they have used a static class and a setter to set a field, and hence injecting mocks using @Inject is unable to create the instance. Is there any way to fix this with changing the API code?

    RunWith(PowerMockRunner.class)
    @PrepareForTest(UserGroup.class)
    public class SomeServiceImplTest {

    @Mock
    private SomeDAOImpl SomeDAOImpl;


    @Mock
    private UserGroup userGroup;


    @InjectMocks
    SomeServiceImpl someServiceImpl;


    @Test
    public void testSomeMethod(){
         String username = "UserToBeTest";
     //Some code

     //     
         verify(SomeDAOImpl).saveUserGroup(any(),any());
     }
}






public class SomeServiceImpl {

private SomeDAOImpl someDAOImpl;
private SomeIndex someIndex;
public void setSomeDAOImpl(SomeDAOImpl someDAOImpl) {
    this.someDAOImpl = someDAOImpl;
}

    public SomeServiceImpl (){
     someIndex; = AFinalClass.init();
     setSomeDAOImpl(new SomeDAOImpl())  
   }
}

I want to prevent SomeServiceImpl() to getting called. Please let me know if there is any way to fix this. Can I mock constructor - But the SomeServiceImpl() gets called before testSomeMethod().


Solution

  • The normal solution around static method calls is to introduce a Factory.

    Instead of

    public class Example {
        private void foo() {
            Instant now = Instant.now();
        }
    }
    

    use

    public class Example {
    
        private final Supplier<Instant> instantSupplier;
    
        public Example(Supplier<Instant> instantSupplier) {
            this.instantSupplier = instantSupplier;
        }
    
        public void foo() {
            Instant now = instantSupplier.get();
            System.out.println(now);
        }
    }
    

    Now you can mock the creation of the Instant.

    @RunWith(MockitoJUnitRunner.class)
    public class ExampleTest {
    
        @Mock
        Supplier<Instant> instantSupplier;
    
        @Test
        public void test() {
            Instant myNow = Instant.parse("2007-12-03T10:15:30.00Z");
            when(instantSupplier.get()).thenReturn(myNow);
    
            new Example(instantSupplier).foo();
        }
    
    }
    

    Output:

    2007-12-03T10:15:30Z