Home > Blockchain >  How to mock a method used inside my class, but it isn't passed as an argument?
How to mock a method used inside my class, but it isn't passed as an argument?

Time:01-03

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.

CodePudding user response:

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 exemple. 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 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.

CodePudding user response:

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...
        }
    }
}
  • Related