Reputation: 23492
Is it possible to use dependency injection with unit tests using Spring Boot? For integration testing @SpringBootTest
start the whole application context and container services. But is it possible to enable dependency injection functionality at unit test granularity?
Here's the example code
@ExtendWith(SpringExtension.class)
public class MyServiceTest {
@MockBean
private MyRepository repo;
@Autowired
private MyService service; // <-- this is null
@Test
void getData() {
MyEntity e1 = new MyEntity("hello");
MyEntity e2 = new MyEntity("world");
Mockito.when(repo.findAll()).thenReturn(Arrays.asList(e1, e2));
List<String> data = service.getData();
assertEquals(2, data.size());
}
}
@Service
public class MyService {
private final MyRepository repo; // <-- this is null
public MyService(MyRepository repo) {
this.repo = repo;
}
public List<String> getData() {
return repo.findAll().stream()
.map(MyEntity::getData)
.collect(Collectors.toList());
}
}
Or should I just manage the SUT (service class) as POJO and manually inject the mocked dependencies? I want to keep tests fast but minimize boilerplate code.
Upvotes: 4
Views: 5199
Reputation: 3265
As @M.Deinum mentioned in the comments, unit tests shouldn't use dependency injection. Mock MyRepository
and inject MyService
using Mockito (and Junit5):
@ExtendWith(MockitoExtension.class)
public class MyServiceTest {
@InjectMocks
private MyService service;
@Mock
private MyRepository repo;
@Test
void getData() {
MyEntity e1 = new MyEntity("hello");
MyEntity e2 = new MyEntity("world");
Mockito.when(repo.findAll()).thenReturn(Arrays.asList(e1, e2));
List<String> data = service.getData();
assertEquals(2, data.size());
}
}
If you want to test the repository, use @DataJpaTest
. From the docs:
Using this annotation will disable full auto-configuration and instead apply only configuration relevant to JPA tests.
@DataJpaTest
public class MyRepositorTest {
@Autowired
// This is injected by @DataJpaTest as in-memory database
private MyRepository repo;
@Test
void testCount() {
repo.save(new MyEntity("hello"));
repo.save(new MyEntity("world"));
assertEquals(2, repo.count());
}
}
In conclusion, the suggested approach is to test the service layer mocking the repository layer with Mockito (or similar library) and to test the repository layer with @DataJpaTest
.
Upvotes: 1
Reputation: 3766
You have not added the @Autowired
in service for MyRepository
Service Class
@Service
public class MyService {
private final MyRepository repo; // <-- this is null
@Autowired
public MyService(MyRepository repo) {
this.repo = repo;
}
public List<String> getData() {
return repo.findAll().stream()
.map(MyEntity::getData)
.collect(Collectors.toList());
}
}
Service Test Class
@ExtendWith(MockitoExtension.class)
public class MyServiceTest {
@Mock
private MyRepository repo;
@InjectMocks
private MyService service;
@Test
void getData() {
MyEntity e1 = new MyEntity("hello");
MyEntity e2 = new MyEntity("world");
Mockito.when(repo.findAll()).thenReturn(Arrays.asList(e1, e2));
List<String> data = service.getData();
assertEquals(2, data.size());
}
}
Upvotes: 0