Unit testing java REST API with request headers, legacy code - java

I need to pass in a non-null HttpServletRequest somehow or control HelperClass.getUser(request) which is throwing an error(the method does request.getHeader() inside and as the request is null it throws an error and fails the test).
We would like to unit test our REST API, so we quickly know if a change (or a developer) breaks it or if we have a bug etc. eventually to start doing TDD/BDD. These tests are destined eventually for use for automated builds by ant. I can call REST API and set headers from java but don't think this would be a unit test? Would depend on running it on local host? Could be wrong about that
Code:
#Path("service/")
public class Service {
static DataAccess da = null;
#javax.ws.rs.core.Context HttpServletRequest request;
//constructor where doa will be mock object
public Service(DataAccess dao){
da = dao;
}
#GET
#Path("custom-object/{date}/{batch}")
#Produces({"application/xml", "application/json", "application/x-protobuf"})
//Method to test
public CustomObject getCustomObject(
#PathParam("date") String date,
#PathParam("batch") int batch,
#QueryParam("type") String type) {
String user = HelperClass.getUser(request); // this is causing me issues
//da will be a mock object
CustomObject returnedVal = da(user,DatatypeConverter.parseDateTime(date).getTime(), batch, artifactType);
return returnedVal;
}
}
Test using junit/mockito (happy to use powermock as a solution) :
#Test
public void testGetCustomObject() {
System.out.println("getCustomObject");
//Arrange
DataAccess da = mock(DataAccess.class);
Service instance = new Service(da);
String date = "2010-04-05T17:16:00Z";
int batch = 0;
String type = "";
CustomObject expResult = null;
//Act
CustomObject result = instance.getCustomObject(date, batch, type);
//Assert
assertEquals(expResult, result);
}
The test passes if I hardcode the String user="123";. I need to get over this problem before writing useful tests. Can anyone give example code to mock/control this line of code to effectively set user as a non-null string (this line String user = HelperClass.getUser(request); is in EVERY API method)

You could try the REST assured library to help unit test REST services.

For constructing HttpServletRequest you could use some mock object like Spring's MockHttpServletRequest.
Or you could refactor your helper class and make the getUser() method non-static and use Mockito to mock it in your unit tests.

Related

how to junit test on restTemplate?

I have a problem to mock restTemplate with Mockito
code want to be test:
public class Feature{
public static String getfeature(String url){
RestTemplate restTemplate = new RestTemplate();
String xml = "\"feature\": 1";
String json = restTemplate.postForObject(url, xml, String.class);
return json;
}
}
junit code:
#Mock
RestTemplate restTemplate=mock(RestTemplate.class);
#Test
public void testGetfeature(){
string testResponse= "\"feature\": 1";
Mockito.when((String)restTemplate.postForObject(
Mockito.any(String.class),
Mockito.any(Map.class),
Mockito.any(Class.class)
)).thenReturn(testResponse);
Feature feature = new Feature();
feature.getfeature("http://mockValue");
}
I set breaking point at feature.getfeature("http://mockValue"). It still try to connect to the remote server. I don't want postForObject to connect to the http://mockValue.
How should I mock the restTemplate to make postForObject not to connect to http://mockValue?
You are creating a new RestTemplate object in getfeature() method. So, mocking RestTemplate has no effect. Either take RestTemplate as an argument in getfeature() method or take it as constructor argument in Feature class.
Then from the test class, you can mock RestTemplate and pass it like below:
Feature feature= new Feature(mockRestTemplate);
feature.getfeature(url);
Or
Feature feature = new Feature();
feature.getfeature(mockRestTemplate, url);
You have to make the necessary changes in the Feature class based on the decision.
Here is the running code sample:
Main class:
public class Feature {
public static String getFeature(String url, RestTemplate restTemplate) {
return restTemplate.postForObject(url, "", String.class);
}
}
Test class:
Notice the way the RestTemplate is mocked and then the response is mocked.
public class FeatureTest {
#Test
public void testFeature() {
RestTemplate restTemplate = Mockito.mock(RestTemplate.class);
Mockito.when(restTemplate.postForObject(Mockito.any(String.class),
Mockito.any(Object.class), Mockito.any(Class.class))).thenReturn("abc");
System.out.println(Feature.getFeature("http://abc", restTemplate));
}
}
The running code sample is also available at github
Feature.java and FeatureTest.java
There is no need to constantly create a RestTemplate object.
You can create one and inject it into the class.
Then, in your unit test, create a mock RestTemplate and inject that.
Edit:
Concerning the static method.
Don't use a static method,
that is terrible.
If you are using Spring (or any other Dependency Injection framework)
just inject an instance of Feature where ever you want to make the rest call.
Also,
in the Spring world Feature is a service class.
Use the #Service annotation and make the method not static.
I think you need to change your code to make your unit test work atleast using Mockito or you have to use some other library like powermock to mock local object instantiation.
1) create a constructor that accepts RestTemplate as argument to inject your mock.
or
2) create some setter method to inject that RestTemplate.
one more way is to create another method where RestTemplate can be passed.
public String getStringAsJson(RestTemplate restTemplate, String url, String xml) {
return restTemplate.postForObject(url, xml, String.class);
}
then in your test:
RestTemplate mockRestTemplate = mock(RestTemplate.class);
when(restTemplate.postForObject(mockurl, mockUrl, mockXml)).thenReturn("Json");
feature.getStringAsJson(mockRestTemplate,mockUrl,mockXml);
how to junit test on restTemplate?
We test what it returns.
Currently your implementation does nothing itself, it simply delegates to a RestTemplate and return its result.
The fiveelements answer describes the implementation (with broad accepted values as parameters) :
#Test
public void testFeature() {
RestTemplate restTemplate = Mockito.mock(RestTemplate.class);
Mockito.when(restTemplate.postForObject(Mockito.any(String.class),
Mockito.any(Object.class), Mockito.any(Class.class))).thenReturn("abc");
System.out.println(Feature.getFeature("http://abc", restTemplate));
}
That doesn't assert the actual behavior : the URL could be erroneous, the body posted or the response could be wrong and so for... and this test will never detect that. Finally, very important things could be wrong in this implementation and we could not detect that. The value of this kind of test is so weak.
Since this method doesn't perform itself logic, this is better suited for an integration test that could assert and catch much more thing/issues in the actual behavior :
#Test
public void testFeature() {
String actualFeature = Feature.getFeature("http://...");
String expectedFeature = ...;
Assertions.assertEquals(expectedFeature, actualFeature);
}
If you use PowerMockito then you can do like following:
No code change needed here for Feature class then you can use this directly if PowerMockito lib available in prj
RestTemplate mockedRestTemplate = PowerMockito.mock(RestTemplate.class);
PowerMockito.whenNew(RestTemplate.class).withAnyArguments().thenReturn(mockedRestTemplate);

Mockito: Is it bad practice to spy on a method within the system under test which relies on a HTTP request?

From reading around, it appears generally bad practice and a sign of code smell when you have to spy on a method that is used by the current method you're unit testing.
For example, I have this method that I'm unit testing:
public MyResponseObject doStuff(MyRequestObject obj) {
WebTarget tar = getServiceClient().target(obj.toString());
Response res = tar.path(someURI).request().post(somejson);
if(response.getStatus() == 200) {
String jsonResp = response.readEntity(String.class);
return convertToObj(jsonResp);
}
}
One way I'm attempting to solve the above is by
Extracting the first two lines (WebTarget, Response) to its own method that returns a Response object.
Creating a mock of Response and stubbing readEntity to return 200 and readEntity to return "OK"
Here's the result:
public MyResponseObject doStuff(MyRequestObject obj) {
Response res = sendRequest(obj.toString());
if(response.getStatus() == 200) {
String jsonResp = response.readEntity(String.class);
return convertToObj(jsonResp);
}
}
//extracted method
public Response sendRequest(String json){
WebTarget tar = getServiceClient().target(someUrl);
return res = tar.path(someURI).request().post(somejson);
}
//My unit test
//sut is the system under test, setup elsewhere
public void testDoStuff() {
MyRequestObject request = ...;
Response respMock = mock(Response.class);
when(respMock.getStatus()).thenReturn(200);
when(respoMock.readEntity()).thenReturn("OK");
MyClass spy = spy(sut);
Mockito.doReturn(respMock).when(spy).sendRequest(requestString);
MyResponseObject response = spy.doStuff(request);
assertEquals(response.toString(),expectedResp);
}
If I do not stub this out, it attempts to do a real HTTP request and returns an invalid URL error because I'm not supplying a real one - I believe this is what I want because I want my unit tests to be independent of some external system.
Is there a better way I should be going about my unit testing?
Yes creating a spy of the class you're testing is bad practice, break the code you're mocking out into another class and mock that, i.e:
public class MyClass {
private final MySender sender;
public MyClass() {
this(new DefaultSender());
}
public MyClass(MySender sender) {
this.sender = sender;
}
public MyResponseObject doStuff(MyRequestObject obj) {
Response res = sender.sendRequest(obj.toString());
if (response.getStatus() == 200) {
String jsonResp = response.readEntity(String.class);
return convertToObj(jsonResp);
}
}
public interface MySender {
Response sendRequest(String json);
}
private static class DefaultSender implements MySender {
public Response sendRequest(String json) {
WebTarget tar = getServiceClient().target(someUrl);
return res = tar.path(someURI).request().post(somejson);
}
}
}
#RunWith(MockitoJUnitRunner.class)
public class MyClassTest {
private MyClass testSubject;
#Mock
private MySender sender;
#Mock
private Response response;
#Test
public void testDoStuff() {
String expectedResp = ...;
MyRequestObject request = ...;
MyResponseObject response = testSubject.doStuff(request);
assertEquals(response.toString(),expectedResp);
}
#Before
public void setup() {
testSubject = new MyClass(sender);
when(sender.sendRequest(anyString()).thenReturn(response);
when(response.getStatus()).thenReturn(200);
when(response.readEntity()).thenReturn("OK");
}
}
Spying an object you are testing has been communicated to me as bad practice, but these warnings come with little to no explanation backing it up. I'm sure, like anything else, it can definitely be abused.
What I have noticed when testing a method that calls another method of the object being tested, that it is better if you can knock out testing of both methods at the same time. While you could mock the second method, to make the testing of the first method easier, you'll still need to go back at some point and test the second method. Personally I support mocking the second method if it leads to cleaner test code. Basically its an option that the universe gives you, and it should not be ruled out for all cases.
In your scenario, my personal preference would be to mock the WebTarget object, versus creating a second internal method. The main reason for this is that you will have to go back and test the second method anyways, so might as well tackle it now. But if you find that your code could be cleaner by separating those first two lines into their own method (or class) because it is re-usable code that is used multiples times, then of course split it out into its own method. In this case, the architecture of the code itself (and not of the testing requirements) dictate the code structure.
When mocking the WebTarget, in this case, it involves with dealing with the builder methods, e.g. .path(someURI).request().post(somejson), which each have to get mocked accordingly. So that's a bit of a pain. If I was doing this, I would probably use integration testing over unit testing if possible. That is to say, have the server that is being contacted up and available so that I can test against it. In our test environment, all the servers remain up so that we can do leverage more integration tests over unit-tests. As the environment grows, this might not be an option, but right now it is and it leads to cleaner integration-tests that knock out a lot of code coverage in less tests.

Spring boot mocked object returning null on call

I am using #RunWith(SpringRunner.class) to writing unit test case to mock the object. I am trying to mock the repository instance which is accepting request object and returning response, but in unit test case implementation I have mocked the repository using #MockBean annotation and register the it's method call using Mockito.when(respository.post(request)).thenReturn(response). But this call is returning null.
I faced similar situation, the problem is given parameter in Mockito.when() block may not be the same as spring generated. I'll explain my case below, hope to help you:
Product product = new Product(..);
Mockito.when(service.addProduct(product)).thenReturn(saveProduct)
When I send a request, spring generates new Project object which has same fields with product but instance is different. That is, Mockito cannot catch when statement. I changed it like below and it's worked:
Mockito.when(service.addProduct(Mockito.any())).thenReturn(savedProduct)
I figured it out. But the solution is still weird to me...
I was facing this issue because, I was instantiating the request and response in #Before annotated method... as describing below.
#Before
public void setup() {
Request reqA = new Request();
reqA.set..(..);
Response res = new Response();
res.set..(..);
Mockito.when(this.respository.post(reqA)).thenReturn(res);
}
#Test
public void test() {
// Creating Request instance again with all same properties.
// Such that this req instance is technically similarly as instantiated in #Before annotated method (above).
// By, implementing the equals and hashCode method.
Request reqB = new Request();
reqB.set..(..);
// Getting res as 'null' here....
Response res = this.service.post(reqB);
}
Since, reqA and reqB are technically similar then why mocked call not returning the same response as registered.
If I moved setup() method code inside test() method every thing starts working !!!!!
I had the same issue here, vsk.rahul's comment helped me a lot.
I was trying to to use a method to return a mock interaction, but not succeeded, turning it into a static method gave me the expected behavior.
Problem:
The method bar.foo() was returning null for any interaction
public void test1() {
doReturn(mockReturn()).when(bar).foo();
}
private String mockReturn() {
return "abc";
}
Solution:
The method bar.foo() is returning abc String for any interaction
public void test1() {
doReturn(mockReturn()).when(bar).foo();
}
private static String mockReturn() {
return "abc";
}

junit test case for spring MVC

we are developing an application using spring mvc framework. I have given all the classes below, please suggest how to write a junit test case for the below scenario.
I want to write a junit test case for the validateAccountInformation(requestDTO) method which is called in validateAccount(..) method of LPAValidator.java class. Below is my junit test case followed by the java classes. Actual call goes from the LPAController.java as shown in the below code.
LPAControllerTest.java
#Test(groups = "manual")
public void submitRequestForLPAAccountTest()
{
// businessCalendar.nextBusinessDay(
// LocalDateHelper.today(), LPAConstants.TWENTY_BUSSINESS_DAYS)
//i want to write the test case for the above commented logic,
//if business days is not equal to twenty days, test should fail.
}
LPAController.java
#RequestMapping(value = "/lpa/{accNumber}/spread, method = RequestMethod.GET)
public #ResponseBody LPAResponseDTO accountSearch(#RequestBody final LPARequestDTO clientrequestBody,
#PathVariable final String accNumber, final HttpServletResponse response)
{
//some logic goes here
final LPAAccountResponse domainResponse = service.submitRequestForLPAAccount(requestBody);
}
LPAServiceImpl.java
#PermitAll
#NonTransactional
public LPAResponse submitRequestForLPAAccount(final LPARequest requestDTO)
{
return lpaRepository.submitRequestForLPAAccount(requestDTO));
}
LPARepository.java
#PermitAll
#NonTransactional
public LPAResponse submitRequestForLPAAccount(final LPARequest requestDTO)
{
//some logic
lpaValidator.validateAccount(requestDTO);
//some logic
}
LPAValidator.java -- java class for validations
#component
class LPAValidator{
#Inject
private BusinessCalendar businessCalendar;
void validateAccount(final LPARequest requestDTO) throws Exception {
try {
validateAccountInformation(requestDTO);
} catch(Exception e){
}
}
private void validateAccountInformation(final LPARequest requestDTO) throws Exception{
final accDate lpaAccDate = requestDTO.getLPADate();
final LocalDate twentyBussinessDays = businessCalendar.nextBusinessDay(
LocalDateHelper.today(), LPAConstants.TWENTY_BUSSINESS_DAYS); //i want to write
//test case for this line of code, if business days given is more than twenty test should fail.
//some logic here
}
Please suggest what needs to be added in LPAControllerTest.java to test the nextBusinessDay(..) as discussed above.
You're trying to write an integration test in which your controller is called, which then calls all the subclasses until the validator is triggered. That is not a traditional 'unit test'.
A traditional unit test would just test the validator straight up, and nothing more.
Nevertheless, when writing an integration test, spring documentation to the rescue
In short, it'll require you to create an applicationcontext with all the necessary scaffolding, and then use a mockMvc call to do a GET on the created application.
If you want to test the validator, use simple mocking framework:
See [http://mockito.org]
Gives you something like this:
#Mock BusinessCalendar businessCalendarMock;
#Mock LPARequest mockRequest;
#Mock accDate mockDate;
#Mock LocalDate mockLocalDate;
#InjectMocks LPAValidator lpaValidator = new LPAValidator();
#Test public void testValidateAccount() {
when(mockRequest.getLPAdate()).thenReturn(mockDate);
when(businessCalendar.nextBusinessDay(LocalDateHelper.today(),LPAConstants.TWENTY_BUSSINESS_DAYS).thenReturn(mockLocalDate);
// continue your test from here
lpaValidator.validateAccount( mockRequest);
verify(businessCalendar).nextBusinessDay(LocalDateHelper.today(),LPAConstants.TWENTY_BUSSINESS_DAYS);
// although if the use of mockLocalDate is integral to your code, it'll probably show before and no verify is necessary;

how to unit test method using "Spring Data JPA" Specifications

I was playing with org.springframework.data.jpa.domain.Specifications, it's just a basic search :
public Optional<List<Article>> rechercheArticle(String code, String libelle) {
List<Article> result = null;
if(StringUtils.isNotEmpty(code) && StringUtils.isNotEmpty(libelle)){
result = articleRepository.findAll(Specifications.where(ArticleSpecifications.egaliteCode(code)).and(ArticleSpecifications.egaliteLibelle(libelle)));
}else{
if(StringUtils.isNotEmpty(code)){
result= articleRepository.findAll(Specifications.where(ArticleSpecifications.egaliteCode(code)));
}else{
result = articleRepository.findAll(Specifications.where(ArticleSpecifications.egaliteLibelle(libelle)));
}
}
if(result.isEmpty()){
return Optional.empty();
}else{
return Optional.of(result);
}
}
And that's actually working fine but I'd like to write unit tests for this method and I can't figure out how to check specifications passed to my articleRepository.findAll()
At the moment my unit test looks like :
#Test
public void rechercheArticle_okTousCriteres() throws FacturationServiceException {
String code = "code";
String libelle = "libelle";
List<Article> articles = new ArrayList<>();
Article a1 = new Article();
articles.add(a1);
Mockito.when(articleRepository.findAll(Mockito.any(Specifications.class))).thenReturn(articles);
Optional<List<Article>> result = articleManager.rechercheArticle(code, libelle);
Assert.assertTrue(result.isPresent());
//ArgumentCaptor<Specifications> argument = ArgumentCaptor.forClass(Specifications.class);
Mockito.verify(articleRepository).findAll(Specifications.where(ArticleSpecifications.egaliteCode(code)).and(ArticleSpecifications.egaliteLibelle(libelle)));
//argument.getValue().toPredicate(root, query, builder);
}
Any idea?
I was having almost the same problems as you had, and I changed my class that contains Specifications to be an object instead of just one class with static methods. This way I can easily mock it, use dependency injection to pass it, and test which methods were called (without using PowerMockito to mock static methods).
If you wanna do like I did, I recommend you to test the correctness of specifications with integration tests, and for the rest, just if the right method was called.
For example:
public class CdrSpecs {
public Specification<Cdr> calledBetween(LocalDateTime start, LocalDateTime end) {
return (root, query, cb) -> cb.between(root.get(Cdr_.callDate), start, end);
}
}
Then you have an integration test for this method, which will test whether the method is right or not:
#RunWith(SpringRunner.class)
#DataJpaTest
#Sql("/cdr-test-data.sql")
public class CdrIntegrationTest {
#Autowired
private CdrRepository cdrRepository;
private CdrSpecs specs = new CdrSpecs();
#Test
public void findByPeriod() throws Exception {
LocalDateTime today = LocalDateTime.now();
LocalDateTime firstDayOfMonth = today.with(TemporalAdjusters.firstDayOfMonth());
LocalDateTime lastDayOfMonth = today.with(TemporalAdjusters.lastDayOfMonth());
List<Cdr> cdrList = cdrRepository.findAll(specs.calledBetween(firstDayOfMonth, lastDayOfMonth));
assertThat(cdrList).isNotEmpty().hasSize(2);
}
And now when you wanna unit test other components, you can test like this, for example:
#RunWith(JUnit4.class)
public class CdrSearchServiceTest {
#Mock
private CdrSpecs specs;
#Mock
private CdrRepository repo;
private CdrSearchService searchService;
#Before
public void setUp() throws Exception {
initMocks(this);
searchService = new CdrSearchService(repo, specs);
}
#Test
public void testSearch() throws Exception {
// some code here that interact with searchService
verify(specs).calledBetween(any(LocalDateTime.class), any(LocalDateTime.class));
// and you can verify any other method of specs that should have been called
}
And of course, inside the Service you can still use the where and and static methods of Specifications class.
I hope this can help you.
If you are writing Unit Tests then you should probably mock the call to findAll() method of articleRepository Class using a mocking framework like Mockito or PowerMock.
There is a method verify() using which you can check if the mock is invoked for the particular parameters.
For Example, if you are mocking the findAll() method of articleRepository Class and want to know if this method is called with particular arguments then you can do something like:
Mokito.verify(mymock, Mockito.times(1)).findAll(/* Provide Arguments */);
This will fail the test if mock has not been called for the arguments that you provided.
Your problem is that you are doing too many things within that one method. You should have three different methods that work on articleRepository.
Then you can use mocking as the others suggest:
setup your mocks so that you know which call on articleRepository should be made
verify that exactly the expected calls are happening
Please note: these three methods should be internal; the main point there is: you can't test this method with ONE call from the outside; as it is doing more than one thing, depending on the input that you provide. Thus you need to create at least one test method for each of the potential paths in your code. And that becomes easier (from a conceptual point of view) when you separate your code into different methods.

Categories

Resources