2

I have the following class:

import name.package.OtherClass;
import name.package.Context;
import name.package.Configuration.keys;

public class MyClass{
    public static void configure(Context context){
       Configuration config = new OtherClass().getConfiguration(keys);
       context.configure(config);
    }
}

I want to mock the method OtherClass.getConfiguration(). Could I do the following?

import org.mockito.Mock;
import org.mockito.Mockito;

public class MyClassTest{
   @Mock
   private Context context  = Mockito.mock(Context.class);
   
   @Mock
   private OtherClass otherClass  = Mockito.mock(OtherClass.class);
   
   @Mock
   private Context configurationMock  = Mockito.mock(Configuration.class);

   @Test
   public void testConfigure(){
      Mockito.when(otherClass.getConfiguration(keys)).thenReturn(configurationMock);
      MyClass.configure(context);
      verify(context.configure(configurationMock), times(1))
   }
}

Remember that OtherClass is not an argument of MyClass, and I want to know if a mock of it inside the class of tests takes effect in MyClass instance when I run the test.

I have tried executing this but didn't get clear results.

Mureinik
  • 297,002
  • 52
  • 306
  • 350
  • Mockito cannot mock static classes or methods. You'll have to user PowerMock(ito). Even better to change your classes' design to be testable :) – knittl Jan 02 '23 at 16:35
  • 1
    @knittl omg actually I've created an example that is not equal to mine in reality. What if OtherClass. getConfiguration() weren't static? Btw I will edit and make it not static. – emanoellucas Jan 02 '23 at 16:39
  • 1
    This might be useful: [Why is my class not calling my mocked methods in unit test?](https://stackoverflow.com/q/74027324/112968) – knittl Jan 02 '23 at 16:41

2 Answers2

3

Modern versions of Mockito allow you to mock constructors.

First, make sure you have the mockito-inline dependency instead of the mockito-core dependency. E.g., in Maven, you'll have something like this:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-inline</artifactId>
    <version>4.10.0</version>
    <scope>test</scope>
</dependency>

Then, you can use the mockConstruction method to replace the constructor of OtherClass, and mock what even behavior you need for it:

class MyClassTest {
    @Mock
    Configuration mockedConfig;
    @Mock
    Context mockedContext;

    @Test
    public void testConfigure() {
        try (MockedConstruction<OtherClass> mockedConstruction = 
             Mockito.mockConstruction(OtherClass.class, (mock, context) -> {
            Mockito.when(mock.getConfiguration(Mockito.any())).thenReturn(mockedConfig);
            // Additional mocking you may need
        })) {
            MyClass.configure(mockedContext);
            // assertions...
        }
    }
}
Mureinik
  • 297,002
  • 52
  • 306
  • 350
  • You can, but _should_ you? Wouldn't it be more beneficial in the middle and long term to design classes to be testable? – knittl Jan 02 '23 at 18:50
  • @knittl, totally agre, but unfortunately it is a legacy android from the company I work on and can't change it :( – emanoellucas Jan 02 '23 at 19:15
  • @knittl that's pretty much the usecase - handling code bases where you can't (or it's unreasonably expensive) to refactor the code and make it testable. – Mureinik Jan 02 '23 at 19:22
2

Could I do the following?

Yes you can do it but you are doing it the wrong way. As your method OtherClass.getConfiguration(keys) is a static method, you will have to write it by following this example. As your method is not static anymore, your code will look like this:

import org.mockito.Mock;
import org.mockito.Mockito;

@ExtendWith(MockitoExtension.class)
public class MyClassTest {

   @Mock
   private Context context;
   
   @Mock
   private OtherClass otherClass;

   @InjectMocks    
   private MyClass myClass;

   @Test
   public void testConfigure(){
      Mockito.when(otherClass.getConfiguration(any(Key.class))).thenReturn(new Configuration());
      MyClass.configure(context);
      verify(context.configure(configurationMock), times(1))
   }

}

then I want to know if a mock of it inside the class of tests takes effect in MyClass instance when I run the test

Yes your mock takes effect in your class, as it is the purpose of the mock to give you the ability to modify the behaviour of your dependencies.

Also, when using the @Mock annotation, you don't need to create a new instance and your test did not define a class undertest with @InjectMocks.

While calling the when method, you will declare the class type of keys with any(Key.class) parameter and you will provide a new instance of configuration.

Harry Coder
  • 2,429
  • 2
  • 28
  • 32
  • I've edited the code, actually the method I want to mock is not static. Thank you very much! I thought that mock would take effect only when injected. – emanoellucas Jan 02 '23 at 16:43
  • Sure, you have to inject the mock in your class `undertest` with `@InjectMocks` annotation. When loading your tests, your mock will be automatically injected. – Harry Coder Jan 02 '23 at 16:45