Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why Mockito @InjectMocks might be a thing to avoid?

Why @InjectMocks might be a thing to avoid for this kind of test.

@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {

    @Mock
    private Bar bar;

    @InjectMocks
    private Foo foo; // created by Mockito

    @Test
    public void shouldCallMethod() {

        // when
        foo.myMethod();

        // then
        ...
    }
}

Foo.java

public class Foo {

    private final Bar bar;

    public Foo(Bar bar) {
        this.bar = bar;
    }
...

I read about this in comments to this answer: https://stackoverflow.com/a/21172873/516167

About @InjectMocks

Mark a field on which injection should be performed.

  • Allows shorthand mock and spy injection.
  • Minimizes repetitive mock and spy injection.

Reference: @InjectMocks JavaDoc.

like image 414
MariuszS Avatar asked Jan 17 '14 09:01

MariuszS


Video Answer


2 Answers

The only disadvantage I see is that you have to comply to its rules. You may have more control if you don't use @InjectMocks

From their docs, I added some in bold:

Mockito will try to inject mocks only either by constructor injection, setter injection, or property injection in order and as described below. If any of the following strategy fail, then Mockito won't report failure; i.e. you will have to provide dependencies yourself.

Constructor injection; the biggest constructor is chosen, then arguments are resolved with mocks declared in the test only. Note: If arguments can not be found, then null is passed. If non-mockable types are wanted, then constructor injection won't happen. In these cases, you will have to satisfy dependencies yourself.

Property setter injection; mocks will first be resolved by type, then, if there is several property of the same type, by the match of the property name and the mock name. Note 1: If you have properties with the same type (or same erasure), it's better to name all @Mock annotated fields with the matching properties, otherwise Mockito might get confused and injection won't happen.

Note 2: If @InjectMocks instance wasn't initialized before and have a no-arg constructor, then it will be initialized with this constructor.

Field injection; mocks will first be resolved by type, then, if there is several property of the same type, by the match of the field name and the mock name. Note 1: If you have fields with the same type (or same erasure), it's better to name all @Mock annotated fields with the matching fields, otherwise Mockito might get confused and injection won't happen.

Note 2: If @InjectMocks instance wasn't initialized before and have a no-arg constructor, then it will be initialized with this constructor.

like image 54
isah Avatar answered Sep 30 '22 16:09

isah


Also, some people sugest that dependencies should be added by constructor, not by setters, this way the correct dependecies are guarantied at compile time. See this.

By using constructors you are forced to limit yourself in the number of components to use as a dependency reducing complexity. (This may not be easy and changes a lot the way to do things in Spring).

Update: After some time working with dependencies autowired in the constructor I have clearly noted that test are less fragile. I still need to change the tests when dependencies change but the compiler tells me where I should change it and how big the change is. Even better, now I can distinguish between changes to the test to adapt to the new code in compile time and failures added by this code in runtime.

like image 31
borjab Avatar answered Sep 30 '22 18:09

borjab