Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

@InjectMocks has null dependencies

We are currently using JUnit and Mockito to test our code.

We have a Service Interface something like below.

public interface ServiceA {
     void doSomething();
}

And its implementation class is like below.

@Service
@Transactional
public class ServiceAImpl implements ServiceA {

     @Inject
     private RepositoryA repA;

     @Inject
     private ShareServiceA sharedServA;

     public void doSomething(){
     }
}

Now, I would just like to mock repA dependency of ServiceAImpl class.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:test-context.xml" })
@DirtiesContext(classMode= DirtiesContext.ClassMode.AFTER_CLASS)
public class ServiceAImplTest {

   @Mock
   RepositoryA repA;

   @InjectMocks
   ServiceA servA = new ServiceAImpl();

   @Before
   public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        ........
   }
}

After calling initMocks, only repA dependency of ServiceImpl was initiated and sharedServA remains null thus causing null exception when the class being tested calls a method of sharedServA.

Based on some references that I've read on the internet and books, this will only happen if the class being tested has constructor with arguments declared. But, for my example above, I didn't declare any constructor. Is this the correct behavior or am I missing something?

like image 660
artsylar Avatar asked Dec 20 '25 17:12

artsylar


1 Answers

Continuing to @daniu answer

You are stuck because of a mixed-match approach.

If you are writing test for a unit you will have to satisfy dependencies of the unit in your test also.

Let's consider you have a class A with two dependencies B and C and your are injecting them old-fashion using setters like

public class A {

    private B b;

    private C c;

    void method() {
        System.out.println("A's method called");
        b.method();
        c.method();

    }

    public void setB(B b) {
        this.b = b;
    }

    public void setC(C c) {
        this.c = c;
    }
}

Then unit test for above class will be like

public class ATest {

    A a=new A();

    @Test
    public void test1() {
        B b=new B();
        C c=new C();
        a.setB(b);
        a.setC(c);

        a.method();
    }
}

dependencies satisfied in test also using setter injection. This was actually the way how java units were tested prior to mockito framework. Here B and C could have been test-doubles or actual classes as per need.

But if we are using annotation based dependency injection in our classes using spring then our A class will look something like

@Service
public class A {

    @Inject
    private B b;

    @Inject
    private C c;

    void method() {
        System.out.println("A's method called");
        b.method();
        c.method();

    }
}

Here we are relying upon @Inject to inject our dependency automatically using spring configurations.

But to unit test these we need some framework such as mockito which is equally capable of doing so i.e. injecting dependency without setter or constructor. Every dependency can be either a mock or a spy(if actual invocation is needed)

Hence test of A should look like

@RunWith(MockitoJUnitRunner.class)
public class ATest {

    @InjectMocks
    A a;

    @Mock //or @Spy
    B b;

    @Mock //or @Spy
    C c;

    @Test
    public void test() {
        a.method();
    }
}

But you can not mix and match. If you do not want to use @InjectMocks and not want to @Spy for every dependency that you want to be actually executed simply @Autowire or instantiate A in your class that will load A with all the actual dependencies but then you will have to think how to override that one or two specific mocks that you want to be mocked.

1) You can provide a setter for that specific dependency and then you can create a mock and insert it.

public class ATest {

    @Autowired
    A a;

    @Mock
    B b;

    @Test
    public void test() {
        a.setB(b);
        a.method();
    }
}

2) You can be a badass and use ReflectionTestUtils to injected mocked dependencies on the fly even if your original class does not provide a setter for it.

@ContextConfiguration(classes=Config.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class ATest {

    @Autowired
    A a;

    @Mock
    B b;

    @Test
    public void test() {
        ReflectionTestUtils.setField(a, "b", b);
        a.method();
    }
}

3) Also as mentioned by @Yogesh Badke you can maintain such mocked beans in a separate test context file and use that but again that file will have to be maintained for every such specific example.

like image 65
Dhawal Kapil Avatar answered Dec 23 '25 05:12

Dhawal Kapil



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!