简体   繁体   中英

How to use Dependency Injection with Spring Boot Unit Testing?

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.

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 .

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());
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM