Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mockito Inject mock into Spy object

I'm writing a test case for a Class which has a 2 level of dependency injection. I use @Spy annotation for the 1 level dependency injection object, and I would like to Mock the 2nd level of injection. However, I kept getting null pointer exception on the 2nd level. Is there any way that I inject the mock into the @Spy object?

public class CarTestCase{
    @Mock
    private Configuration configuration;

    @Spy 
    private Engine engine;

    @InjectMocks 
    private Car car;

    @Test
    public void test(){

       Mockito.when(configuration.getProperties("")).return("Something");
       car.drive();
    }

}

public class Car{
    @Inject
    private Engine engine;

    public void drive(){
        engine.start();
    }
}

public class Engine{
    @Inject 
    private Configuration configuration;

    public void start(){
        configuration.getProperties();   // null pointer exception
    }

}
like image 316
Wildchild Avatar asked May 11 '17 22:05

Wildchild


People also ask

Can I use InjectMocks and spy together?

@Spy and @InjectMocks cannot be used well together (see Google Code issue #489 and GitHub issue #169), and for what they do it is not clear or common that they should be used together at all. In well-written Mockito usage, you generally should not even want to apply them to the same object.

How do you mock injected objects?

Mockito @InjectMocks annotations allow us to inject mocked dependencies in the annotated class mocked object. This is useful when we have external dependencies in the class we want to mock. We can specify the mock objects to be injected using @Mock or @Spy annotations.

How do you inject an mock for an interface?

Here is what works: public class TestDo { @Mock private Do do; @Mock private ABC abc; @Before public void init() { MockitoAnnotations. initMocks(this); do. abc = abc; } @Test public void testDo() { when(do.

How do you mock a method in Spy class?

Mockito spy() method When using the spy method, there exists a real object, and spies or stubs are created of that real object. If we don't stub a method using spy, it will call the real method behavior. The main function of the spy() method is that it overrides the specific methods of the real object.


3 Answers

I've also wandered how to inject a mock into a spy.

The following approach will not work:

@Spy
@InjectMocks
private MySpy spy;

But the desired behavior can be achieved by a "hybrid" approach, when using both annotation and manual mocking. The following works perfectly:

@Mock
private NeedToBeMocked needToBeMocked;

@InjectMocks
private MySpy mySpy;

@InjectMocks
private SubjectUnderTest sut;

@BeforeMethod
public void setUp() {
    mySpy = Mockito.spy(new MySpy());
    MockitoAnnotations.initMocks(this);
}

(SubjectUnderTest here depends on MySpy, and MySpy in its turn depends on NeedToBeMocked).

UPD: Personally, I think that if you have to do such a magic too often, it might be a sign that there is something wrong with dependenicies between your classes and it is worth to perform a little bit of refactoring to improve your code.

like image 197
Yoory N. Avatar answered Sep 22 '22 18:09

Yoory N.


Mockito cannot perform such a tricky injections as it's not an injection framework. So, you need to refactor your code to make it more testable. It's easy done by using constructor injection:

public class Engine{
    private Configuration configuration;

    @Inject 
    public Engine(Configuration configuration) {
        this.configuration = configuration;
    }
    ........
}

public class Car{
    private Engine engine;

    @Inject    
    public Car(Engine engine) {
        this.engine = engine;
    }
}

In this case you have to handle the mocking and injection manually:

public class CarTestCase{

    private Configuration configuration;

    private Engine engine;

    private Car car;

    @Before
    public void setUp(){
        configuration = mock(Configuration.class);
        engine = spy(new Engine(configuration));
        car = new Car(engine);
    }

    @Test
    public void test(){

       Mockito.when(configuration.getProperties("")).return("Something");
       car.drive();
    }

}
like image 31
Sergii Bishyr Avatar answered Sep 20 '22 18:09

Sergii Bishyr


The (simplest) solution that worked for me.

@InjectMocks
private MySpy spy = Mockito.spy(new MySpy());

No need for MockitoAnnotations.initMocks(this) in this case, as long as test class is annotated with @RunWith(MockitoJUnitRunner.class).

like image 29
Ahmad Shahwan Avatar answered Sep 22 '22 18:09

Ahmad Shahwan