In a project where I work, we have been initializing Services for Unit testing in the following way:
- Mock dependencies that are needed for the Service.
- Create the Service using a constructor.
Something like this :
@RunWith(SpringRunner.class)
public class ServiceTest extends AbstractUnitTest {
@Mock private Repository repository;
private Service service;
@Before
public void init() {
service = new Service(repository);
when(repository.findById(any(Long.class))).thenReturn(Optional.of(new Entity()));
}
}
But our new developer proposed to use @Autowired
and @SpringBootTest
@SpringBootTest(classes = ServiceTest.class)
@MockBean(classes = Repository.class)
@RunWith(SpringRunner.class)
public class ServiceTest extends AbstractUnitTest {
@MockBean private Repository repository;
@Autowired private Service service;
@Before
public void init() {
when(repository.findById(any(Long.class))).thenReturn(Optional.of(new Entity()));
}
}
Before that, I supposed that @Autowired
and @SpringBootTest
should be used only in integration tests. But googled a lot and I see that some people use those two in Unit tests.
I read boot-features-testing. Also, I read this Unit tests vs integration tests with Spring.
For me, it still doesn`t feel well that we need to involve Spring to do dependency injection for unit tests, as we can do this by ourselves to do the unit testing.
So, should @Autowired
and @SpringBootTest
be used in unit tests?
No. A unit test is to test a single component in isolation. Using constructor injection in your beans allows you to very simply call new SomeService(myMock)
, no Spring required.
Writing component or functional tests (testing your application but not wiring it up to external services for a full integration test, mocking only external interfaces; this is good for things like MockMvc tests) is a good match for @SpringBootTest
, and in that case you may need to create mock objects in a Spring configuration and autowire them into your test so you can manipulate them.