I am writing some unit tests and I am confused due to the different annotations in unit tests:
One of them is like:
#ExtendWith(SpringExtension.class)
class EmployeeServiceImplTest {
#MockBean
private EmployeeRepository employeeRepository;
#Autowired
private EmployeeServiceImpl employeeService;
#Test
void testFindAll() {
//...
}
//...
}
And another is using the following annotations:
#RunWith(MockitoJUnitRunner.class)
public class EmployeeServiceImplTest {
#Mock
private EmployeeRepository employeeRepository;
#InjectMocks
private EmployeeServiceImpl employeeService;
#Test
void testFindAll() {
//...
}
//...
}
So, I use Java and Spring Boot for language and framework. So, which annotations should I use?
#ExtendWith(SpringExtension.class) or #RunWith(MockitoJUnitRunner.class),
#MockBean or #Mock,
#Autowired or #InjectMocks
Any idea regarding to these annotations?
Related
Seeing an issue with spy and auto-wire along with Extend(SpringExtention)
Argument should be a mock, but is: class
org.springframework.data.jpa.repository.support.SimpleJpaRepository
#ExtendWith(SpringExtention.class)
#SpringBootTest(classes = TestServer.class)
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
#ActiveProfiles("test")
#Transactional
SampleServiceTest {
#Spy
#Autowire
private TestRepo repo;
#Mock
SpecialTest test;
#Mock
SpecialTest1 test1;
#InjectMock
TestServiceTest testService;
#InjectMock
SampleServiceTest sampleServiceTest;
#BeforeEach()
void setup () {
openMocks(this);
when(testService.getId()).thenReturn(test);
when(testService.getId()).thenReturn(test1);
}
#Test
void test () {
}
When I am using the spring-boot: 2.4.1 I am seeing the above issue.
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());
}
}
I have a problem when I want to test controllers using #WebMvcTest - on application load I get:
...
Field customUserDetailsService in com.test.config.SecurityConfig required a bean of type 'com.test.security.CustomUserDetailsService' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.test.security.CustomUserDetailsService' in your configuration.
...
Do you have any idea what is problem? Is it good solution to exclude security?
There are classes:
#RestController
#AllArgsConstructor
public class EmployeeController {
private final EmployeeService employeeService;
#GetMapping
public List<EmployeeDTO> getEmployees() {
return employeeService.getAllEmployees();
}
}
#WebMvcTest(controllers = EmployeeController.class)
class EmployeeControllerTest {
#Autowired
private MockMvc mockMvc;
#InjectMocks
private EmployeeController employeeController;
#Test
void getEmployees_Success() throws Exception {
// mockMvc.perform(get("/employee/all")).andExpect(status().isOk());
}
The controller which you are using in test have a dependency EmployeeService employeeService;
So in test case add below.
#Mock
EmployeeService employeeService;
How to write JUnit Test cases for RestController, Service and DAO layer?
I've tried MockMvc
#RunWith(SpringRunner.class)
public class EmployeeControllerTest {
private MockMvc mockMvc;
private static List<Employee> employeeList;
#InjectMocks
EmployeeController employeeController;
#Mock
EmployeeRepository employeeRepository;
#Test
public void testGetAllEmployees() throws Exception {
Mockito.when(employeeRepository.findAll()).thenReturn(employeeList);
assertNotNull(employeeController.getAllEmployees());
mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/employees"))
.andExpect(MockMvcResultMatchers.status().isOk());
}
How can I verify the CRUD methods inside the rest controller and other layers ?
You can use #RunWith(MockitoJUnitRunner.class) for unit testing with your Service Layer mocking your DAO Layer components. You don't need SpringRunner.class for it.
Complete source code
#RunWith(MockitoJUnitRunner.class)
public class GatewayServiceImplTest {
#Mock
private GatewayRepository gatewayRepository;
#InjectMocks
private GatewayServiceImpl gatewayService;
#Test
public void create() {
val gateway = GatewayFactory.create(10);
when(gatewayRepository.save(gateway)).thenReturn(gateway);
gatewayService.create(gateway);
}
}
You can use #DataJpaTest for integration testing with
your DAO Layer
#RunWith(SpringRunner.class)
#DataJpaTest
public class GatewayRepositoryIntegrationTest {
#Autowired
private TestEntityManager entityManager;
#Autowired
private GatewayRepository gatewayRepository;
// write test cases here
}
Check this article for getting more details about testing with Spring Boot
could you help me please,
some code:
#ContextConfiguration(locations = { "/applicationContext.xml" })
#RunWith(SpringJUnit4ClassRunner.class)
public class TestUnit2 {
#Mock
private MongoOperations mongoTemplate;
#InjectMocks
#Autowired
private WorkcircleRepositoryMongoImpl workCircleRepository;
#Autowired
private WorkcircleServiceImpl workCircleServiceImpl;
#Before
public void setUp() {
....
when(mongoTemplate.findOne(new Query(), Person.class)).thenReturn(expectedPerson);
MockitoAnnotations.initMocks(this);
}
#Test
public void test() {
... workCircleServiceImpl.find()...
}
But test is failed:
NP in "... workCircleServiceImpl.find()..." line,
in separate way #InjectMocks & #Autowired work, but together are not worked.
Usually when you are unit testing, you shouldn't initialize Spring context.
So remove Autowiring.
Usually when you do integration testing, you should use real dependencies.
So remove mocking.
You are mixing integration and unit test here.