I have a problem with testing (mocking the value for method) my delete method from controller . In ordinary mode it works fine but not when I test.
Here is my code.
My Controller
#RestController
#RequestMapping("/")
public class MainController {
#DeleteMapping(value = "/deletePost/{id}")
public ResponseEntity<String> deletePost(#PathVariable int id) throws SQLException {
boolean isRemoved = postsService.deletePost(connection, id);
if (!isRemoved)
return new ResponseEntity<>("Post with given id was not found", HttpStatus.NOT_FOUND);
else {
modifiedPostsService.insertModificationData(connection, new ModificationData(id, "deletion"));
return new ResponseEntity<>("Post with given id has been deleted.", HttpStatus.OK);
}
}
}
My PostsService
public boolean deletePost(Connection connection, int id) throws SQLException {
return postsDao.deletePost(connection, id);
}
My PostsDao
#Override
public boolean deletePost(Connection connection, int id) throws SQLException {
boolean isPostExists = isPostExist(connection, id);
PreparedStatement ps;
ps = connection.prepareStatement("delete from POSTS where ID = " + id);
ps.executeUpdate();
return isPostExists;
}
And finally my tests
#WebMvcTest(MainController.class)
class MainControllerTests {
#Autowired
private MockMvc mockMvc;
#MockBean
private Connection connection;
#MockBean
private PostsService mockPostsService;
#Test
void testIfDeletePostUrlIsOk() throws Exception {
Mockito.when(mockPostsService.deletePost(connection, 1)).thenReturn(true);
mockMvc.perform(MockMvcRequestBuilders
.delete("/deletePost/1")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
}
testIfDeletePostUrlIsOk() returns 404 instead of 200 ( I guess mocking value - true not works, is false instead). Why and how to solve that?
#SpringBootTest
#AutoConfigureMockMvc
public class TestingWebApplicationTest {
#Autowired
private MockMvc mockMvc;
#MockBean
Connection mockConnection;
#MockBean
PostsService mockPostsService;
#Test
void testIfDeletePostUrlIsOk() throws Exception {
Mockito.when(mockPostsService.deletePost(any(), anyInt())).thenReturn(true);
mockMvc.perform(MockMvcRequestBuilders
.delete("/deletePost/1")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
}
You need to inject your mocks into controller, annotation #SpringBootTest and #MockBean will do the work
when you use:
Mockito.mock(...)
you are creating a mock object on your local scope, you have to inject it to your SUT or use:
#MockBean
MockBean will make your object accessible by spring's ApplicationContext so spring can pick your mock.
Related
I need to test this method :
#PostMapping(path = { API.PATH.SEND, API.PATH.PUBLIC_SEND })
public ResponseEntity<MessageDto> send(#RequestBody MessageDto messageDto) {
log.info("Sending message {}", messageDto.getMessage());
final var sendedMessageId = this.envoyerMessage.execute(
WebMessageMapper.INSTANCE.messageDtoToMessage(messageDto)
);
messageDto.setId(sendedMessageId.getValue());
return ResponseEntity.status(HttpStatus.CREATED).body(messageDto);
}
So I created this test class :
#Tag("unitTest")
#WebMvcTest(MessageControllerV1.class)
public class MessageControllerV1Test {
#Autowired
private MockMvc mockMvc;
#MockBean
private EnvoyerMessage envoyerMessage;
#MockBean
private LireMessage lireMessage;
private WebMessageMapper webMessageMapper;
#Test
public void send() throws Exception {
String content = "{ \"id\":1\"\", \"message\": \"Test variable\" }";
this.mockMvc.perform(
post("/api/message/add")
.contentType(MediaType.APPLICATION_JSON)
.content(content)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().is(200));
}
}
But I get NullPointerException on sendedMessageId.
How does it work to integrate Mapper mock in this test ?
How to pass a ingredientGroup? Or is there some other way?
Controller:
#Controller
#RequestMapping("/ingredients/groups")
#RequiredArgsConstructor
#PermissionUserWrite
public class IngredientGroupController {
private static final String VIEWS_PATH = "/pages/ingredient/group/";
private final IngredientGroupService ingredientGroupService;
#GetMapping("{id}")
public String show(#PathVariable("id") IngredientGroup group, Model model) {
if (group == null) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Ingredient group not found");
}
model.addAttribute("group", group);
return VIEWS_PATH + "show";
}
}
Test:
#SpringBootTest
#AutoConfigureMockMvc
class IngredientGroupControllerTest {
private static final String VIEWS_PATH = "/pages/ingredient/group/";
#Autowired
private MockMvc mockMvc;
#Test
#WithMockAdmin
void show_for_admin() throws Exception {
var ingredientGroup = Mockito.mock(IngredientGroup.class);
mockMvc.perform(MockMvcRequestBuilders.get("/ingredients/groups/{id}", 1))
.andExpect(status().isOk())
.andExpect(view().name(VIEWS_PATH+"show"));
}
}
I don't what the fields are in IngredientGroup. But, I presume that there are fields name and something.
When using an object as #PathVariable, you are expected to pass its properties as query parameters. So, in your case the url you are about to test looks like:
http://localhost:8080/ingredients/groups/1?name=xxxxxx&something=otherthing
#Test
public void show_for_admin() throws Exception {
var ingredientGroup = Mockito.mock(IngredientGroup.class);
mockMvc.perform(MockMvcRequestBuilders.get(String.format("/ingredients/groups/%d", 1),
ingredientGroup.getName(),
ingredientGroup.getSomething()))
.andExpect(status().isOk());
}
I've made rest controller, that calls #service class:
#Service
public class UnitServiceImpl extends HttpRequestServiceImpl implements UnitService {
#Override
public Unit addUnit(String unitName) {
final Unit unit = new Unit();
unit.setUnitName(unitName);
return unitRepository.save(unit);
}
#Override
public Unit getUnit(int id) {
final Unit unit = unitRepository.findById(id);
if (unit == null) {
throw new EntityNotFoundException("Unit is not found");
}
return unit;
}
#Override
public Iterable<Unit> getAllUnits() {
return unitRepository.findAll();
}
}
EnityNotFoundException is handled by ExceptionHandlingController:
#RestController
#ControllerAdvice
public class ExceptionHandlingController extends ResponseEntityExceptionHandler {
#ExceptionHandler({RuntimeException.class})
public final ResponseEntity<ErrorDetails> handleRuntimeException(RuntimeException ex, WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(),
request.getDescription(false));
HttpStatus httpStatus = HttpStatus.BAD_REQUEST;
if (ex.getClass() == EntityNotFoundException.class) {
httpStatus = HttpStatus.NOT_FOUND;
}
return new ResponseEntity<>(errorDetails, httpStatus);
}
}
Unit controller just calls the getUnit:
#RestController
public class UnitController {
private final UnitService managementService;
#PostMapping(value = "/unit", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Unit> addUnit(HttpServletRequest request) throws FieldsIsAbsentException {
final String unitName = managementService.getParameter(request, "unit_name");
final Unit unit = managementService.addUnit(unitName);
return new ResponseEntity<>(unit, HttpStatus.CREATED);
}
public UnitController(UnitService managementService) {
this.managementService = managementService;
}
#GetMapping(value = "/unit", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Iterable<Unit>> getAllUnits() {
final Iterable<Unit> allUnits = managementService.getAllUnits();
return new ResponseEntity<>(allUnits, HttpStatus.OK);
}
#GetMapping(value = "/unit/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Unit> getUnitById(#PathVariable("id") int id) {
final Unit unit = managementService.getUnit(id);
return new ResponseEntity<>(unit, HttpStatus.CREATED);
}
}
Now I need to test them, and created unit test method, that must to check on 404 error:
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ContextConfiguration
class UnitControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
UnitService unitService;
#MockBean
UnitRepository unitRepository;
#Autowired
private UnitController unitController;
private List<Unit> units;
#Before
public void initUnits() {
units = new ArrayList<>();
Unit unitWithName = new Unit();
unitWithName.setId(1);
unitWithName.setUnitName("NameUnit");
units.add(unitWithName);
Unit unitWithoutName = new Unit();
unitWithoutName.setId(2);
units.add(unitWithoutName);
}
#Test
void contextLoads() {
Assert.assertNotNull(unitController);
}
#Test
void testGetAllUnits() throws Exception {
given(this.unitService.getAllUnits()).willReturn(units);
mockMvc.perform(get("/unit"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON));
}
#Test
void testUnitNotFound() throws Exception {
int id = -1;
given(this.unitRepository.findById(id)).willReturn(null);
mockMvc.perform(get("/unit/-1"))
.andExpect(status().isNotFound())
.andExpect(content().contentType(MediaType.APPLICATION_JSON));
}
}
When I run tests, testGetAllUnits fails:
java.lang.AssertionError: Content type not set
and testUnitNotFound fails with error:
java.lang.AssertionError: Status expected:<404> but was:<201>
But when I remove
#MockBean
UnitService unitService;
It will be working. What the problem?
UPDATE:
I have the similar problem now. This code inserts into database info about unit. But I made mock for the method.
#Test
void testAddUnit() throws Exception {
Unit unit = new Unit();
unit.setId(1);
unit.setUnitName("TestUnit");
given(unitService.addUnit("TestUnit")).willReturn(unit);
mockMvc.perform(post("/unit").param("unit_name", "TestUnit"))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.unitName").value("TestUnit"))
.andExpect(jsonPath("$.id").value(1));
}
You're mocking the wrong bean. The bean throwing the exception is the service bean, so mock that.
#Test
void testUnitNotFound() throws Exception {
int id = -1;
given(this.service.getUnit(id)).willThrow(new EntityNotFoundException("Unit is not found"));
mockMvc.perform(get("/unit/-1"))
.andExpect(status().isNotFound())
.andExpect(content().contentType(MediaType.APPLICATION_JSON));
}
The problem with the testUnitNotFound() test not working is that you are expecting something from the mocked repository to happen inside a service which is also mocked.
If the service is mocked, then no implementation is invoked. Only a default value is returned which is null. And therefore no exception is thrown as expected...
If you want to have the flexibility of having most of the service mocked but having rest of them having their original implementations called, then you should change the:
#MockBean
UnitService unitService;
into
#SpyBean
UnitService unitService;
I had some trouble setting up unit test with my spring boot application. My main issue is with the "model" object that's needed in my controller, but I can't find a way to recreate it in my test, which is required to use my function.
here are the function I want to test
#Controller
public class AjoutAbscenceControler {
#Autowired
private AbsenceRepository absenceRepository;
#RequestMapping(value = { "/addAbsence" }, method = RequestMethod.GET)
public String showAddAbsencePage(Model model) {
Absence absence = new Absence();
model.addAttribute("Absence", absence);
return "addAbsence";
}
#RequestMapping(value = { "/addingAbsence" }, method = RequestMethod.POST)
public String saveAbsence(Model model, #ModelAttribute("absence") Absence absence) {
if (absence.getName() != null && absence.getName().length() > 0) {
absenceRepository.save(absence);
}
return "redirect:/userList";
}
}
I did try something like that
#RunWith(MockitoJUnitRunner.class)
public class AjoutAbscenceControlerTest {
#Mock
VacationRepository vacationRepository;
#Mock
CategoryRepository categoryRepository;
#InjectMocks
AjoutAbscenceControler controler;
public MockMvc mockMvc;
#Before
public void setUp() throws Exception{
mockMvc = MockMvcBuilders.standaloneSetup(controler).build();
}
#Test
public void showAddAbsencePagetest() {
AjoutAbscenceControler ajoutAbscenceControler =new AjoutAbscenceControler();
assertEquals("addAbsence",ajoutAbscenceControler.showAddAbsencePage(controler));
}
}
but I don't find any way to create a springfarmwork.ui.Model
If you're testing the logic of your controller you probably shouldn't create a Model object, but mock it, and verify the interactions against it:
#Mock
private Model model;
#Test
public void showAddAbsencePagetest() {
// Should probably be initialized in a #Before method,
// Initialized here for clarity only
AjoutAbscenceControler ajoutAbscenceControler = new AjoutAbscenceControler();
assertEquals("addAbsence", ajoutAbscenceControler.showAddAbsencePage(model));
Mockito.verify(model).addAttribute(eq("Absence"), any(Absence.class));
}
I have a controller
#RestController
public class Create {
#Autowired
private ComponentThatDoesSomething something;
#RequestMapping("/greeting")
public String call() {
something.updateCounter();
return "Hello World " + something.getCounter();
}
}
I have a component for that controller
#Component
public class ComponentThatDoesSomething {
private int counter = 0;
public void updateCounter () {
counter++;
}
public int getCounter() {
return counter;
}
}
I also have a test for my controller.
#RunWith(SpringRunner.class)
#SpringBootTest
public class ForumsApplicationTests {
#Test
public void contextLoads() {
Create subject = new Create();
subject.call();
subject.call();
assertEquals(subject.call(), "Hello World 2");
}
}
The test fails when the controller calls something.updateCounter(). I get a NullPointerException. While I understand it's possible to add #Autowired to a constructor I would like to know if there is anyway to do this with an #Autowired field. How do I make sure the #Autowired field annotation works in my test?
Spring doesn't auto wire your component cause you instantiate your Controller with new not with Spring, so Component is not instatntiated
The SpringMockMvc test check it correct:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
public class CreateTest {
#Autowired
private WebApplicationContext context;
private MockMvc mvc;
#Before
public void setup() {
mvc = MockMvcBuilders
.webAppContextSetup(context)
.build();
}
#Test
public void testCall() throws Exception {
//increment first time
this.mvc.perform(get("/greeting"))
.andExpect(status().isOk());
//increment secont time and get response to check
String contentAsString = this.mvc.perform(get("/greeting"))
.andExpect(status().isOk()).andReturn()
.getResponse().getContentAsString();
assertEquals("Hello World 2", contentAsString);
}
}
The #Autowired class can be easily mocked and tested with MockitoJUnitRunner with the correct annotations.
With this you can do whatever you need to do with the mock object for the unit test.
Here is a quick example that will test the Create method call with mocked data from ComponentThatDoesSomething.
#RunWith(MockitoJUnitRunner.class)
public class CreateTest {
#InjectMocks
Create create;
#Mock
ComponentThatDoesSomething componentThatDoesSomething;
#Test
public void testCallWithCounterOf4() {
when(componentThatDoesSomething.getCounter()).thenReturn(4);
String result = create.call();
assertEquals("Hello World 4", result);
}
}
Use Mockito and inject a mock that you create. I would prefer constructor injection:
#RestController
public class Create {
private ComponentThatDoesSomething something;
#Autowired
public Create(ComponentThatDoesSomething c) {
this.something = c;
}
}
Don't use Spring in your Junit tests.
public CreateTest {
private Create create;
#Before
public void setUp() {
ComponentThatDoesSomething c = Mockito.mock(ComponentThatDoesSomething .class);
this.create = new Create(c);
}
}