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));
}
Related
I want to write a unit test for a controller that uses a FormFactory object
My controller:
public class LoginController extends ApiController {
private final FormFactory formFactory;
#Inject
public LoginController(FormFactory formFactory) {
this.formFactory = formFactory;
}
public Result login(Http.Request request) {
DynamicForm form = formFactory.form().bindFromRequest(request);
String username = form.get("username");
String password = form.get("password");
doSomething();
return result;
}
}
In my test class I'll have to mock the external dependency FormFactory and to stub .form() and .bindFromRequest(request) as well, but I don't know how.
My test class is:
public class LoginControllerTest {
#InjectMocks
private LoginController controller;
#Mock
private FormFactory formFactory;
#Before
public void setup() {
MockitoAnnotations.openMocks(this);
}
#Test
public void login_ok() {
Map<String, String> formData = new HashMap<>();
formData.put("username", "user");
formData.put("password", "pass");
Http.RequestBuilder fakeRequest = new Http.RequestBuilder().method(Helpers.POST).bodyForm(formData);
when(formFactory.form()).thenReturn(new DynamicForm(???));
Result result = controller.adminLogin(fakeRequest.build());
}
}
If I test my class I got a NPE because .form() is null in LoginController class.
How Can I solve this issue?
What parameters I have to pass in new DynamicForm(???)?
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;
Below is main code consist of one util class and service class using it
#PropertySource("classpath:atlas-application.properties")
public class ApacheAtlasUtils {
#Value("${atlas.rest.address}")
private String atlasURL;
#Value("${atlas.rest.user}")
private String atlasUsername;
#Value("${atlas.rest.password}")
private String atlasPassword;
private AtlasClientV2 client;
public AtlasClientV2 createClient() {
if (client == null) {
return new AtlasClientV2(new String[] {atlasURL}, new String[] {atlasUsername, atlasPassword});
} else {
return client;
}
}
}
Service Class is below :-
#Override
public Page<SearchResultDto> findFilesWithPages(QueryParent queryParent, Pageable pageable)
throws AtlasServiceException {
// Some code
client = new ApacheAtlasUtils().createClient();
//some code
}
I am writing unit test for service method and I am getting exception for createClient method asking for values for url, username and password which should not happen as this should be mocked but the mocking is giving me below error
java.lang.IllegalArgumentException: Base URL cannot be null or empty.
at com.google.common.base.Preconditions.checkArgument(Preconditions.java:141)
at org.apache.atlas.AtlasServerEnsemble.<init>(AtlasServerEnsemble.java:35)
at org.apache.atlas.AtlasBaseClient.determineActiveServiceURL(AtlasBaseClient.java:318)
at org.apache.atlas.AtlasBaseClient.initializeState(AtlasBaseClient.java:460)
at org.apache.atlas.AtlasBaseClient.initializeState(AtlasBaseClient.java:448)
at org.apache.atlas.AtlasBaseClient.<init>(AtlasBaseClient.java:132)
at org.apache.atlas.AtlasClientV2.<init>(AtlasClientV2.java:82)
at com.jlr.stratus.commons.utils.ApacheAtlasUtils.createClient(ApacheAtlasUtils.java:40)
at com.jlr.stratus.rest.service.impl.FileSearchService.findFilesWithPages(FileSearchService.java:49)
The Test code is as follows:-
private FileSearchService fileSearchService;
#Spy
private ApacheAtlasUtils apacheAtlasUtils;
#Mock
private AtlasClientV2 client;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
fileSearchService = new FileSearchService();
}
#Test
public void findFilesWithPages_searchAll() throws AtlasServiceException {
Mockito.doReturn(client).when(apacheAtlasUtils).createClient();
service.search(queryParent,pageable);
}
Your idea with spying is adequate (you can even go for mocking if you do not actually need any true implementation of that class).
The problem lies in the implementation:
// Some code
client = new ApacheAtlasUtils().createClient();
//some code
}
Instead of having the ApacheAtlasUtils as an instance variable (or a supplier method) you create the instance on the fly.
Mockito is not smart enough to catch that operation and replace the real object with you spy.
With the supplier method you can set up your test as follows:
#Spy
private FileSearchService fileSearchService = new FileSearchService();
#Spy
private ApacheAtlasUtils apacheAtlasUtils = new ApacheAtlasUtils();
#Mock
private AtlasClientV2 client;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
doReturn(apacheAtlasUtils).when(fileSearchService).getApacheUtils();
}
in your SUT:
#Override
public Page<SearchResultDto> findFilesWithPages(QueryParent queryParent, Pageable pageable)
throws AtlasServiceException {
// Some code
client = getApacheUtils().createClient();
//some code
}
ApacheAtlasUtils getApacheUtils(){
return new ApacheAtlasUtils();
}
This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 3 years ago.
I am new to Java, tried various ways to resolve this but there is no luck.
The TestFeedService interface object (testService) value is showing null on testService.sendMessage(dataBO, false) call. This is there inside the method I'm calling from the test case. I am not sure why this is happening. I tried creating Object in the setup() but still it is failing. Can someone please help me in this.
Below is the Test case code
public class testApiControllerTest {
private MockMvc mockMvc;
private TesApiController testApiController;
private TestFeedService testService;
#Before
public void setup() {
testApiController = mock(TestApiController.class);
testService = mock(TestFeedService.class);
testService = new TestFeedServiceImpl();
}
#Test
public void testProcessMessage() {
TestApiController testApiController = new TestApiController();
TestForm2 testForm2 = new TestForm2();
testForm2.setType("PROMO_CODE");
testForm2.setUserId("1");
testForm2.setMessage("Welcome");
ResponseEntity<?> responseEntity = testApiController.processMessage(testForm2);
assertEquals(HttpStatus.OK, responseEntity.getStatusCode());
}}
Java Code:
public class TestApiController {
#Autowired
private TestFeedService testService; // This is an interface
#RequestMapping(path = "/api/process-message", method = RequestMethod.POST)
public ResponseEntity<?> processMessage(#RequestBody TestForm2 testForm) {
DataBO dataBO = convertBO(testForm);
testService.sendMessage(dataBO, false); // here I am getting testService = null and causing exception
return ResponseEntity.ok().build();
} // Some more code
You need to put the TestService in the constructor of TestApiController, so you can inject it when creating the object in the test.
public class TestApiController {
private TestFeedService testService; // This is an interface
#Autowired
public TestApiController(TestFeedService testService ) {
this.testService = testService;
}
#RequestMapping(path = "/api/process-message", method = RequestMethod.POST)
public ResponseEntity<?> processMessage(#RequestBody TestForm2 testForm) {
DataBO dataBO = convertBO(testForm);
testService.sendMessage(dataBO, false); // here I am getting testService = null and causing exception
return ResponseEntity.ok().build();
} // Some more code
So your test would be like:
public class testApiControllerTest {
private MockMvc mockMvc;
private TesApiController testApiController;
private TestFeedService testService;
#Before
public void setup() {
this.testService = mock(TestFeedService.class);
}
#Test
public void testProcessMessage() {
this.testApiController = new TestApiController(this.testService);
// More code
}}
Hope it helps
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);
}
}