0

I am testing a service that has an autowired helper component. That component has autowired repo.

In my test, I want to use that component helper, not a mock. And I want to mock the repo for that.

But I can't manage to make it work.

The Service that I test:

@Service
public class ServiceImpl{
    @Autowired
    private Helper helper;
}

The Helper class that has autowired repo

@Component
public class Helper {
    @Autowired
    private Repository repo;
}

My test should be like this

@ExtendWith(MockitoExtension.class)
public class ServiceImplTest {

    ServiceImpl service;

    @Mock
    private Repository repoMock;

    @InjectMocks
    private Helper helper;

}

I'd like better to refactor the whole thing but unfortunately, it's not possible...

Any help welcome.

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
moguil
  • 83
  • 1
  • 7

4 Answers4

2

I would prefer constructor injection over field injection. (read more here)

In this case you classes would look something like this:

    @Component
    public class Helper {
        @Autowired
        public Helper(Repository repo) {
            this.repo = repo;
        }
    }

    @Service
    public class ServiceImpl{
        @Autowired
        public ServiceImpl(Helper helper) {
            this.helper = helper;
        }
    }

This way you can easily create a real Helper object with a mock Repository object:

    ServiceImpl service;

    private Helper helper;

    @Mock
    private Repository repoMock;

    @BeforeEach
    void init() {
        helper = new Helper(repoMock);
        service = new ServiceImpl(helper);
    }
Rashin
  • 726
  • 6
  • 16
2

I finally found a solution, thanks for the help.

@ExtendWith(MockitoExtension.class)
public class ServiceImplTest {

    @InjectMocks
    ServiceImpl service

    @Spy
    @InjectMocks
    private Helper helper;

    @Mock
    private Repository repoMock;

    @InjectMocks
    private Helper helper;
}

That way, the mocked repo is injected in the spy helper, and the helper can be injected in the service. The @Spy objects are actually instantiated, so if you don't stubb any of its methods, you'll get a "real" object.

Here, the mocked repo is injected in the helper and the helper injected in the service.

moguil
  • 83
  • 1
  • 7
0

If Repository is an interface (and not a concrete class) you can try the following:

@ExtendWith(MockitoExtension.class)
public class ServiceImplTest {

    @Spy
    @InjectMocks
    ServiceImpl service = new ServiceImpl();

    @Mock
    private Repository repoMock;

    @InjectMocks
    private Helper helper;
}
Lorelorelore
  • 3,335
  • 8
  • 29
  • 40
  • Problem remains the same. The helper is null when used in the service because it's not properly injected (not a mock) – moguil Mar 27 '19 at 15:35
  • 1
    actually the solution was close to yours but it's the helper that should be annotaed with @Spy. – moguil Mar 28 '19 at 16:42
0

Try to load a configuration for your tests which gives priority to a mock repo Tested:

@RunWith(SpringRunner.class)
@SpringBootTest
public class SomeTest {

    @Configuration
    static class ContextConfiguration {     

        @Bean
        public Helper helper() {
            return new Helper();
        }
        @Bean
        @Primary
        public Repository repoMock() {
            Repo repo = Mockito.mock(Repository.class);
            Mockito.when(/* Mock your repo */);
            return repo;
        }
    }

    @Autowired
    private Helper helper;

    @Test
    public void testMethod() {
        // Your test goes here
    }
}

Anyway, keep in mind that field autowiring is evil. Switch to constructor dependency injection ASAP.

See also:

Aritz
  • 30,971
  • 16
  • 136
  • 217