3
votes

Im not new to mockito, but this time I found an interesting case during my work. I hope you can help me out with it.

I need to inject mock to change certain method behaviour during the test. The problem is, the bean structure is nested, and this bean is inside other beans, not accessible from test method. My code looks like this:

@Component
class TestedService { 
  @Autowired
  NestedService nestedService;
}

@Component
class NestedService {
  @Autowired
  MoreNestedService moreNestedService;
}

@Component
class MoreNestedService {
  @Autowired
  NestedDao nestedDao;
}

@Component
class NestedDao {
  public int method(){
    //typical dao method, details omitted
  };
}

So in my test I would like the call NestedDao.method to return mocked answer.

class Test { 
  @Mock
  NestedDao nestedDao;

  @InjectMocks
  TestedService testedSevice;

  @Test
  void test() {
    Mockito.when(nestedDao.method()).thenReturn(1);
    //data preparation omitted
    testedSevice.callNestedServiceThatCallsNestedDaoMethod();
    //assertions omitted
  }
}

I have tried to do a initMocks:

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

Also to add annotation upon my test class:

@RunWith(MockitoJUnitRunner.class)

Always getting nullpointers or wrong answer from method (not mocked).

I guess it's this nested calls fault, making it impossible to mock this Dao. Also Ive read that @InjectMocks only would work with setters or constructor injection, which im missing (just @Autowire on private fields), but it didn't worked when I tried.

Any guess what am I missing? ;)

2
You're not mocking the nested service, so how should that work? And one normally don't mock the nestedDao, call when(nestedService.callDaoMethod()).return("stuff which the dao should return") instead.Tom
Okay, how can I mock nested service, and then mock something nested in it and again the same? There are no examples of doing as such over the internet. Another way I see is to use constructors and create objects, but the dependancied are way too complicated to me (Im new in project). It would be nice if i could just tell that class NestedDao has to return another object as normally would. This call is deep in hierarchy, so this is hard to mock this all nested services.Rafał
"and then mock something nested in it and again the same" There is no reason to do that. This is a unit test. You're testing TestedService and not implementation details of NestedService and its nested dependencies. For example your class MoreNestedService ... this class doesn't even need to be mentioned in the test. You only mock TestedService and tell that mock what to return on a certain method call. That this method may call nested methods "in real life" doesn't matter right now.Tom
Btw: please create a minimal reproducible example. Your classes are already ok, but without knowing that you're testing, what the test data is and what you assert, it is quite hard to tell you what the problem is. You still can use abstract data and class names, but the example should reproduce your current problem.Tom
OKay I will do that when I have more time :) Thanks for your help so far.Rafał

2 Answers

7
votes

You could use @MockBean instead of @Mock and @InjectionMock.

@RunWith(SpringRunner.class)
@SpringBootTest
class Test { 
  @MockBean
  NestedDao nestedDao;

  @Autowired
  TestedService testedSevice;

  @Test
  void test() {
    Mockito.when(nestedDao.method()).thenReturn(1);
    //data preparation omitted
    testedSevice.callNestedServiceThatCallsNestedDaoMethod();
    //assertions omitted
  }
}
0
votes

It makes sense to me, you are so wrong, why? It's because you're testing TestedService and the interaction with NestedService, not with the Dao, the Dao interaction should be verified on the NestedService tests

Look this:

@Component
class TestedService { 

  @Autowired
  NestedService nestedService;

  String sayHello(String name){
     String result = "hello" + nestedService.toUpperCase(name)
  }
}

@Component
class NestedService {

  @Autowired
  MoreNestedService moreNestedService;

  String toUpperCase(String name){
        String nameWithDotAtTheEnd = moreNestedService.atDot(name);
        return nameWithDotAtTheEnd.toUpperCase();

  }

}

On your test:

class Test { 
  @Mock
  NestedService nestedService;

  @InjectMocks
  TestedService testedSevice;

  @Test
  void test() {
    Mockito.when(nestedService.toUpperCase("rene")).thenReturn("RENE.");
    //data preparation omitted
    Assert.assertEquals("hello RENE.", testedSevice.sayHello("rene"));
    //assertions omitted
  }
}

As you can see, you are assuming the dependencies of TestedService are working well, you only need to verify that hello is being added as prefix of the string,