Reputation: 141
I have created a Spring Boot Project with RestController. I am looking to test the RestController in SpringBoot. But I am unable to inject interface in Spring Controller Test. I am getting below error.
Please assist.
Cannot instantiate @InjectMocks field named 'authService'! Cause: the type 'AuthService' is an interface.
You haven't provided the instance at field declaration so I tried to construct the instance.
Examples of correct usage of @InjectMocks:
@InjectMocks Service service = new Service();
@InjectMocks Service service;
//and... don't forget about some @Mocks for injection :)
My Source Code is as follows
AuthService Interface
public interface AuthService {
AppUser save(RegisterDto registerDto);
}
AuthServiceImpl.java
@Service
public class AuthServiceImpl implements AuthService {
@Autowired
private AppUserRepository userRepository;
@Override
public AppUser save(RegisterDto registerDto) {
if (userRepository.existsByUsername(registerDto.getUsername())) {
throw new RuntimeException("Username already taken");
}
if (userRepository.existsByEmail(registerDto.getEmail())) {
throw new RuntimeException("Email address already registered");
}
AppUser user = convertToAppUser(registerDto);
return userRepository.save(user);
}
private AppUser convertToAppUser(RegisterDto registerDto) {
AppUser user = new AppUser(
registerDto.getFirstName(),
registerDto.getLastName(),
registerDto.getUsername(),
registerDto.getEmail(),
registerDto.getPassword());
return user;
}
}
AuthController.java
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private AuthService authService;
@PostMapping("/users")
public AppUser save(@RequestBody RegisterDto registerDto) {
return authService.save(registerDto);
}
}
AuthControllerTest.java
@RunWith(SpringRunner.class)
@WebMvcTest(AuthController.class)
public class AuthControllerTest {
@Autowired
MockMvc mockMvc;
@InjectMocks
private AuthService authService;
@Test
public void save_user__success() throws Exception {
AppUser user = new AppUser("admin", "admin", "admin", "[email protected]", "admin");
when(authService.save(Mockito.any(RegisterDto.class))).thenReturn(user);
mockMvc.perform(
MockMvcRequestBuilders.post("/api/auth/users")
.contentType(MediaType.APPLICATION_JSON)
.content(convertToJson(user)))
.andExpect(status().isCreated());
}
/*
* converts a Java object into JSON representation
*/
public static String convertToJson(final Object obj) {
try {
return new ObjectMapper().writeValueAsString(obj);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Upvotes: 1
Views: 2141
Reputation: 135762
You would use @InjectMocks
only in a concrete class object instance (whose real functionality you want to use, only with its dependencies mocked), not on an interface (which generally doesn't have functionality, just a contract, nor an instance).
Since you use when(authService.save(...))).thenReturn(...)
, what I believe you want is actually @MockBean
, so use it instead of @InjectMocks
:
@RunWith(SpringRunner.class)
@WebMvcTest(AuthController.class)
public class AuthControllerTest {
@Autowired
MockMvc mockMvc;
@MockBean // replaced @InjectMocks
private AuthService authService;
@Test
public void save_user__success() throws Exception {
AppUser user = new AppUser("admin", "admin", "admin", "[email protected]", "admin");
when(authService.save(Mockito.any(RegisterDto.class))).thenReturn(user);
mockMvc.perform(
// ...
Upvotes: 2