Help
#Override
public String postRequestinTransactionService(String data) {
RequestTransaction request = new RequestTransaction(data.getClass().getName(), data);
HttpEntity<RequestTransaction> entity = new HttpEntity<RequestTransaction>(request);
ResponseEntity<String> response = restTemplate.exchange(this.urlTransactions, HttpMethod.POST, entity,
String.class);
return response.getBody();
}
Here is barebone test class for you. You can write test case and if you have specific problem then ask question.
#RunWith(MockitoJUnitRunner.class)
public class YourClassNameTest{
#InjectMocks
YourClassUnderTest myClass;
private String data;
#Before
public void setUp() throws Exception {
//prepare you data here
// any other mock action you can set here
}
#Test
public void testPostRequestinTransactionService() throws Exception {
//Write you test here
String result=myClass.postRequestinTransactionService(data);
assertThat("result should be blablabla", result, is("blablabla");
}
Related
So I am working with a working UI, and using a DB2 database. I am trying to run unit testing on the controller/service/dao layers and I am using mockito and junit to test. Here are the pieces of each layer:
Measures.java
#Controller
#RequestMapping(value = "/measures"})
public class Measures {
#Resource
private CheckUpService checkUpService;
public void setCheckUpService(CheckUpService checkUp) {
this.checkUpService = checkUpService;
}
#RequestMapping(value = "/eligibility/{userId}/{effDate}/{stageInd}", method = RequestMethod.POST, produces = "application/json")
public #ResponseBody List<Model> findEligibility(#PathVariable int userId, #PathVariable String effDate, #PathVariable String stageInd) throws Exception
{
List<Model> result = new ArrayList<Model>();
if (stageInd.equals("stage"))
{
result = checkUpService.findEligibilityStage(userId, effDate);
}
if (stageInd.equals("prod"))
{
result = checkUpService.findEligibility(userId, effDate);
}
return result;
}
...
}
CheckUpService.java
public class CheckUpService {
#Resource
EligibilityDao eligDao;
public List<Model> findEligibility(int userId, String effDate) throws Exception
{
return eligDao.findEligibility(userId, effDate, db_table);
}
}
EligibilityDao.class
public class EligibilityDao {
public List<Model> findEligibility(int userId, String effDate, String table) throws Exception
{
// uses some long sql statement to get some information db2 database and
// jdbctemplate helps return that into a list.
}
}
Here is the controller test that I am trying to do, I've spent about 10 hours on this and I really can't figure out why it's giving me a 406 error instead of 200.
ControllerTest.java
#EnableWebMvc
#WebAppConfiguration
public class ControllerTest {
#Autowired
private Measures measures;
#Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
private List<Model> findEligibility() {
List<Model> list = new ArrayList<>();
Model test_model = new Model();
test_model.setUserId(99);
test_model.setCreateID("testUser");
test_model.setEffDate("2020-07-30");
list.add(test_model);
return list;
}
#Before
public void setup() throws Exception {
this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
public void test_find() throws Exception {
CheckUpService checkUpService = mock(CheckUpService.class);
when(checkUpService.findEligibility(99, "2020-07-30")).thenReturn(findEligibility());
measures.setCheckUpService(checkUpService);
String URI = "/measures/eligibility/99/2020-07-30/prod";
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.post(URI).contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON);
MvcResult handle = mockMvc.perform(requestBuilder).andReturn();
// MvcResult handle = mockMvc.perform(requestBuilder).andExpect(status().isOk()).andReturn();
MvcResult result = mockMvc.perform(asyncDispatch(handle)).andExpect(status().isOk()).andReturn();
// assertThat(result.getResponse().getContentAsString()).isEqualTo(findEligibility());
}
}
The MvcResult result is what is throwing the "StatusExpected <200> but was <406>" error in junit and i'm going mad on why it is. Another issue is that, if you can see, I commented out the handle with the .andExpect(status().isOk()) and that one was also throwing the same issue. Is it something i'm setting up wrong with the test or something?
I could not reproduce your issue. However, I got this working.
So, no big changes to the Controller, but I removed the field injection in favor of constructor injection.
#Controller
#RequestMapping(value = "/measures")
public class Measures {
private final CheckUpService checkUpService;
public Measures(CheckUpService checkUpService) {
this.checkUpService = checkUpService;
}
#RequestMapping(value = "/eligibility/{userId}/{effDate}/{stageInd}", method = RequestMethod.POST, produces = "application/json")
public #ResponseBody List<Model> findEligibility(#PathVariable int userId, #PathVariable String effDate, #PathVariable String stageInd) throws Exception {
List<Model> result = new ArrayList<>();
if (stageInd.equals("stage")) {
result = checkUpService.findEligibility(userId, effDate);
}
if (stageInd.equals("prod")) {
result = checkUpService.findEligibility(userId, effDate);
}
return result;
}
}
The same for the service class.
#Service
public class CheckUpService {
private final EligibilityDao dao;
public CheckUpService(EligibilityDao dao) {
this.dao = dao;
}
public List<Model> findEligibility(int userId, String effDate) throws Exception {
return dao.findEligibility(userId, effDate, "demo value");
}
}
and here's the test. Instead of initializing the MockMvc and injecting the web context, I inject MockMvc. Also, using #MockBean, you can inject mocks. So, I removed the mock creation from the test method to the initialization part.
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class MeasuresTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private CheckUpService service;
#Test
public void test_find() throws Exception {
Model model = new Model(99, "2020-08-02", "2020-07-30");
when(service.findEligibility(99, "2020-07-30"))
.thenReturn(Collections.singletonList(model));
String URI = "/measures/eligibility/99/2020-07-30/prod";
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.post(URI)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON);
final MvcResult mvcResult = mockMvc.perform(requestBuilder)
.andExpect(status().isOk()).andReturn();
final String json = mvcResult.getResponse().getContentAsString();
final List<Model> models = new ObjectMapper().readValue(json, new TypeReference<>() {
});
Assert.assertEquals(1, models.size());
Assert.assertEquals(model, models.get(0));
}
}
In the post, I could not find any reason why you used asyncDispatch, so I just did not use it.
In below example i am trying to test both catch and try block using mockito. And also when the CustomException is raised then i want to call the method with second host. Thank you.
private static void getDetails()
{
final String host1 = "http://localhost:8080/springrestexample/employee/id";
final String host2 = "http://localhost:8080/springrestexample/student/id";
RestTemplate restTemplate = new RestTemplate();
String result = null;
try {
String result = restTemplate.getForObject(host1, String.class);
} catch(CustomException customException) {
String result = restTemplate.getForObject(host2, String.class);
}
return result;
}
Would it be possible to have restTemplate being passed to that method as an arg.
(If yes, then)
Using org.mockito.Mockito -> mock() you can mock like below,
RestTemplate restTemplateMock = mock(RestTemplate.class);
when(restTemplateMock.getForObject(host1)).thenThrow(CustomException.class);
Pass this mock object to your method and it will throw exception inside try.
Update: Sample test cases for your ref.
#Test
public void testGetDetails()
{
RestTemplate restTemplateMock = mock(RestTemplate.class);
when(restTemplateMock.getForObject(host1)).thenReturn(SOME_STRING);
String result = //call your method
assertThat(result).isEqualTo(SOME_STRING);
}
#Test
public void testGetDetailsUsesOtherHostWhenExceptionIsThrown()
{
RestTemplate restTemplateMock = mock(RestTemplate.class);
when(restTemplateMock.getForObject(host1)).thenThrow(CustomException.class);
when(restTemplateMock.getForObject(host2)).thenReturn(SOME_STRING);
String result = //call your method
assertThat(result).isEqualTo(SOME_STRING);
}
When you in need to use same mock object,
RestTemplate myRestTemplateMock = mock(RestTemplate.class);
#Test
public void testGetDetails()
{
when(myRestTemplateMock.getForObject(host1)).thenReturn(SOME_STRING);
String result = //call your method
assertThat(result).isEqualTo(SOME_STRING);
}
#Test
public void testGetDetailsUsesOtherHostWhenExceptionIsThrown()
{
when(myRestTemplateMock.getForObject(host1)).
thenThrow(CustomException.class);
when(myRestTemplateMock.getForObject(host2)).thenReturn(SOME_STRING);
String result = //call your method
assertThat(result).isEqualTo(SOME_STRING);
}
Ok, First of all, your original method needs to be like below. (since normal practice is we don't test private methods and if you need a static method to be tested you need something more than Mockito (PowerMock))
public class Example {
#Autowired
public RestTemplate restTemplate;
public String restTemplateTest()
{
final String host1 = "http://localhost:8080/springrestexample/employee/id";
final String host2 = "http://localhost:8080/springrestexample/student/id";
String result;
try {
result = restTemplate.getForObject(host1, String.class);
} catch(CustomException customException) {
result = restTemplate.getForObject(host2, String.class);
}
return result;
}
}
Below is the sample test for the above method.
public class ExampleTest {
#Mock
private RestTemplate restTemplate;
#InjectMocks
private Example testExample;
#BeforeEach
public void setUp()
{
MockitoAnnotations.initMocks(this);
}
#Test
public void restTemplateTest() {
Mockito.when(restTemplate.getForObject(Mockito.eq("http://localhost:8080/springrestexample/employee/id"), Mockito.eq(String.class)))
.thenThrow(CustomException.class);
Mockito.when(restTemplate.getForObject(Mockito.eq("http://localhost:8080/springrestexample/student/id"), Mockito.eq(String.class)))
.thenReturn("expected");
testExample.restTemplateTest();
// if exception thrown , then rest template mock will invoke 2 times, otherwise
// 1
Mockito.verify(restTemplate, Mockito.times(2)).getForObject(Mockito.anyString(), Mockito.eq(String.class));
}
}
Below is the way to test when no Exception occurs scenario.
#Test
public void restTemplateTest_when_no_exception() {
Mockito.when(restTemplate.getForObject(Mockito.eq("http://localhost:8080/springrestexample/employee/id"), Mockito.eq(String.class)))
.thenReturn("expectedString");
String actual = testExample.restTemplateTest();
// if exception not thrown , then rest template mock will invoke 1 times, otherwise
// 1
Mockito.verify(restTemplate, Mockito.times(1)).getForObject(Mockito.anyString(), Mockito.eq(String.class));
Assert.assertEquals("expectedString",actual);
}
If I try to test the post() endpoint, I see:
java.lang.AssertionError: No value at JSON path "$.firstName"
Caused by: java.lang.IllegalArgumentException: json can not be null or empty
But with the test for the get() all work fine.
And in the postTest() the result for status is correct.
Where is my mistaker?
Is it correct way to test the rest controller in this style?
#RunWith(MockitoJUnitRunner.Silent.class)
public class Temp {
private final Employee successfullyRegisteredEmployee = new Employee(2L, "Iven");
private final Employee employeeGetById = new Employee(2L, "Iven");
#Mock
private EmployeeServiceImpl serviceMock;
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.standaloneSetup(new EmployeeControllerImpl( serviceMock))
.build();
}
#Test
public void getTest() throws Exception {
when(serviceMock.getEmployee(2L)).thenReturn(employeeGetById);
mockMvc.perform(get("/employee/get/2"))
.andExpect(status().is(200))
.andExpect(content().json(("{'firstName':'Iven'}")));
verify(serviceMock).getEmployee(2L);
}
#Test
public void postTest() throws Exception {
String json = "{\n" +
" \"firstName\": \"Iven\"\n"
"}";
when(serviceMock.register(employeeForRegister)).thenReturn(successfullyRegisteredEmployee);
mockMvc.perform( MockMvcRequestBuilders
.post("/employee/register")
.content(json)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().is(201))
.andExpect(jsonPath("$.firstName", Matchers.is("Iven")));
}
}
#RestController
#RequestMapping("/employee")
public class EmployeeControllerImpl implements EmployeeController {
private final EmployeeService service;
public EmployeeControllerImpl(EmployeeService service) {
this.service = service;
}
#PostMapping(path = "/register",
consumes = "application/json",
produces = "application/json"
)
public ResponseEntity<Employee> registerEmployee(#Valid #RequestBody Employee employee) {
Employee registeredEmployee = service.register(employee);
return ResponseEntity.status(201).body(registeredEmployee);
}
}
Seems like problem could be with when(serviceMock.register(employeeForRegister)).thenReturn(successfullyRegisteredEmployee);.
Did you try to have breakpoint on return ResponseEntity.status(201).body(registeredEmployee); to check if registeredEmployee is actually filled?
If it's empty then try replacing mock with when(serviceMock.register(any())).thenReturn(successfullyRegisteredEmployee); and if it works that means either equals() method is not overridden for Employee or comparison just fails.
I'm trying to test a rest call that is a part of an mvc controller.
My unit test is currently returning a 404 error code, instead of a 200 status code, which would determine that the request was sent successfully.
Here's the signature of my method that I'm trying to test:
#PreAuthorize("hasRole('ROLE_SSL_USER')")
#PostMapping(value = "/employee", consumes = MediaType.ALL_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
#ResponseStatus(HttpStatus.CREATED)
public ResponseEntity<Object> postEmployee(HttpEntity<String> httpEntity, #RequestHeader("DB-Client-Id") String clientId,
#RequestHeader("X-Forwarded-Client-Dn") String dn) throws IOException, ValidationException {}
Here's my unit test class:
public class ControllerTest {
#InjectMocks
private Controller aController;
private MockMvc mockMvc;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(aController).build();
}
#Test
public void PostEmpTest() {
try {
this.mockMvc.perform(post("/employee")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Am I missing something from my perform() call that is resulting in the 404 bad request code?
I use for controller tests code like this
#RunWith(SpringRunner.class)
#WebMvcTest(Controller.class)
#AutoConfigureWebClient
public class ControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void entityTypes() throws Exception {
String json = "...";
mockMvc.perform(
post("URL")
.contentType(APPLICATION_JSON_UTF8)
.content(json))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().contentType(APPLICATION_JSON_UTF8))
;
}
}
Try it - I hope, it will help.
PS: also, I'm not sure, but it looks like you need add #RequestBody to your controller method declaration:
public ResponseEntity<Object> postEmployee(
#RequestBody HttpEntity<String> httpEntity,
#RequestHeader("DB-Client-Id") String clientId,
#RequestHeader("X-Forwarded-Client-Dn") String dn) throws IOException, ValidationException {}
I'm creating the base for my unit testing project( Spring boot rest controller) and I'm having a problem passing #InjectMocks value because it's only evaluated in #Before and therefore a nullpointer is thrown when i try to access it outside
Some tips to get around the problem please?
Thank you very much
Ps : Any other advices on best practices or something i did wrong for unit testing regarding my current base class test will be appreciated as well
Class to test (rest controller)
#RestController
#RequestMapping("/management")
#Api(description = "Users count connections", produces = "application/json", tags = {"ConnectionManagement API"})
public class ConnectionManagementControllerImpl implements ConnectionManagementController {
#Autowired
private ConnectionManagementBusinessService connectionManagementBusinessService;
#Override
#PostMapping(value = "/countConnectionsByInterval" , consumes = MediaType.TEXT_PLAIN_VALUE , produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
#ApiOperation(value = "count all users connections by interval")
public ResponseEntity<List<ConnectionsCountDto>> countConnectionsByInterval(#RequestBody String format) {
List<ConnectionsCountDto> connectionManagement = connectionManagementBusinessService.countConnectionsByInterval(format);
return new ResponseEntity<List<ConnectionsCountDto>>(connectionManagement, HttpStatus.OK);
}
Abstract base test
public abstract class AbstractBaseTest<C> {
public MockMvc mockMvc;
private Class<C> clazz;
private Object inject;
protected abstract String getURL();
protected final void setTestClass(final Class<C> classToSet, final Object injectToSet) {
clazz = Preconditions.checkNotNull(classToSet);
inject = Preconditions.checkNotNull(injectToSet);
}
#Before
public void init() throws Exception {
MockitoAnnotations.initMocks(clazz);
mockMvc = MockMvcBuilders.standaloneSetup(inject).build();
}
protected MockHttpServletResponse getResponse(MediaType produces) throws Exception {
MockHttpServletResponse response = mockMvc.perform(
get(getURL()).
accept(produces)).
andReturn().
getResponse();
return response;
}
protected MockHttpServletResponse postResponse(String content , MediaType consumes , MediaType produces) throws Exception {
MockHttpServletResponse response = mockMvc.perform(
post(getURL()).
content(content).
contentType(consumes).
accept(produces)).
andReturn().
getResponse();
return response;
}
}
Test class
#RunWith(MockitoJUnitRunner.class)
public class ConnectionManagementControllerImplTest extends AbstractBaseTest<ConnectionManagementControllerImpl>{
#Mock
private ConnectionManagementBusinessService connectionManagementBusinessServiceMocked;
#InjectMocks
private ConnectionManagementControllerImpl connectionManagementControllerMocked;
public ConnectionManagementControllerImplTest() {
super();
setTestClass(ConnectionManagementControllerImpl.class , connectionManagementControllerMocked); // null pointer there
}
#Test
public void countConnectionsByInterval() throws Exception {
// given
given(connectionManagementBusinessServiceMocked.countConnectionsByInterval(Mockito.anyString()))
.willReturn(new ArrayList<ConnectionsCountDto>());
// when
MockHttpServletResponse response = postResponse("day" , MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON_UTF8);
// then
assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());
}
#Override
protected String getURL() {
return "/management/countConnectionsByInterval";
}
This works as intended. However, you can setup mocks manually and inject them inside ConnectionManagementControllerImplTest constructor (before calling setTestClass(...)):
public ConnectionManagementControllerImplTest() {
super();
connectionManagementBusinessServiceMocked = Mockito.mock(ConnectionManagementBusinessService.class);
connectionManagementControllerMocked = new ConnectionManagementControllerImpl();
connectionManagementControllerMocked.setConnectionManagementBusinessService(connectionManagementBusinessServiceMocked);
setTestClass(ConnectionManagementControllerImpl.class, connectionManagementControllerMocked);
}
Do not forget to remove #Mock and #InjectMocks annotations. Btw you can even remove #RunWith(MockitoJUnitRunner.class) in that case.
UPDATE: Both the constructor of test class and "init" method annotated with #Before are executed for each test. The difference is that Mockito annotations are processed between constructor and #Before method invocations.
So you can slightly change your code in order to achieve a positive result:
Create "init" method (annotated with #Before) inside ConnectionManagementControllerImplTest and move setTestClass() into it from the constructor (in that particular case you can also remove the whole constructor because it would contain only super() invocation).
Add super.init() after setTestClass() line (otherwise "init" method in the parent class will be ignored by JUnit).
(Optional) you could also remove #Before annotation from the "init" method in the parent class if your tests are written in the same manner.
The example of code refactored in that way:
public abstract class AbstractBaseTest<C> {
public MockMvc mockMvc;
private Class<C> clazz;
private Object inject;
protected abstract String getURL();
protected final void setTestClass(final Class<C> classToSet, final Object injectToSet) {
clazz = Preconditions.checkNotNull(classToSet);
inject = Preconditions.checkNotNull(injectToSet);
}
#Before //this annotation can be removed
public void init() throws Exception {
MockitoAnnotations.initMocks(clazz); //this line also can be removed because MockitoJUnitRunner does it for you
mockMvc = MockMvcBuilders.standaloneSetup(inject).build();
}
protected MockHttpServletResponse getResponse(MediaType produces) throws Exception {
MockHttpServletResponse response = mockMvc.perform(
get(getURL()).
accept(produces)).
andReturn().
getResponse();
return response;
}
protected MockHttpServletResponse postResponse(String content , MediaType consumes , MediaType produces) throws Exception {
MockHttpServletResponse response = mockMvc.perform(
post(getURL()).
content(content).
contentType(consumes).
accept(produces)).
andReturn().
getResponse();
return response;
}
}
#RunWith(MockitoJUnitRunner.class)
public class ConnectionManagementControllerImplTest extends AbstractBaseTest<ConnectionManagementControllerImpl> {
#Mock
private ConnectionManagementBusinessService connectionManagementBusinessServiceMocked;
#InjectMocks
private ConnectionManagementControllerImpl connectionManagementControllerMocked;
//constructor can be removed
public ConnectionManagementControllerImplTest() {
super();
}
#Before
public void init() throws Exception {
setTestClass(ConnectionManagementControllerImpl.class, connectionManagementControllerMocked);
super.init();
}
#Test
public void countConnectionsByInterval() throws Exception {
// given
given(connectionManagementBusinessServiceMocked.countConnectionsByInterval(Mockito.anyString()))
.willReturn(new ArrayList<ConnectionsCountDto>());
// when
MockHttpServletResponse response = postResponse("day", MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON_UTF8);
// then
assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());
}
#Override
protected String getURL() {
return "/management/countConnectionsByInterval";
}
}
P.S. I'd prefer the former approach, but if you don't want to have a setter for ConnectionManagementBusinessService, you can choose the latter. I've tested both of them and the result was the same.