Our project is built with spring boot. Within this project, we've built lot of REST API implementations using the java language, with REST APIs being invocable at URI end-points. Many of these REST API implementations, interact with RDBMS database at back-end (we're using postgresql). The response of all our REST APIs is JSON.
I'm writing API tests that test these REST APIs, using java within the test case, and using JUnit library to write these tests.
I'm writing two kinds of these API tests, as follows,
1) For the REST APIs, that only do read from the database, the test case implementation is simple. I use an HTTP client library from java within my test case, and issue a GET or POST request. I then do 'assert' on the returned JSON response within the test case.
2) For the REST APIs, that do one of create, update or delete on the database, I'm facing challenges to roll back from the database create, update or delete changes that my test case does. In fact, at this moment, I don't know how to roll back create, update or delete changes that my test case does, just before my test case exits. Can anyone please give pointers how to solve this?
Below is also mentioned present source code of one of my test case,
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.io.InputStream;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.json.JSONArray;
import org.json.JSONObject;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
public class ApplnCommonRestControllerTest {
private static String API_URI_2 = "http://localhost:8081/appln-restapis/rest/public/common/getalluniversities";
private static CloseableHttpClient fHttpClient;
#Before
public void setUp() throws Exception {
fHttpClient = HttpClients.createDefault();
}
#After
public void tearDown() throws Exception {
fHttpClient.close();
}
#Test
public void getAllUniversitiesResponseBodyTest() throws ClientProtocolException, IOException {
HttpGet httpGet = new HttpGet(API_URI_2);
HttpResponse httpResponse = fHttpClient.execute(httpGet);
InputStream inpStream = (httpResponse.getEntity()).getContent();
String respBodyJsonString = Utilities.inputStreamToString(inpStream);
JSONObject jsonResponeObj = new JSONObject(respBodyJsonString);
JSONArray dataJsonArray = jsonResponeObj.getJSONArray("data");
assertTrue(dataJsonArray.length() >= 2); // test that array contains at least two elements
JSONObject arrElem = (JSONObject)dataJsonArray.get(0); // test first element of array
int univId = arrElem.getInt("id");
String univName = arrElem.getString("universityname");
assertTrue(univId == 1 && univName.length() > 0);
arrElem = (JSONObject)dataJsonArray.get(dataJsonArray.length() - 1);
// test last element of array
univId = arrElem.getInt("id");
univName = arrElem.getString("universityname");
assertTrue(univId > 1 && univName.length() > 0);
}
}
The above sample test case code from our project, does an HTTP GET database read only call. I'm looking for a database roll back solution for the test case, in case the REST API invoked by the test case did a database create/update/delete (i.e, just before or after the test case method exited, the database create/update/delete changes should roll back).
Generally I tend to mark all my #Test methods as #Transnational. But as your test case is completely detached from application context of course this will not help. spring has great integration test library for web layer MockMvc. Using this library your test will look like:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.transaction.annotation.Transactional;
import static org.hamcrest.Matchers.*;
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class ApplnCommonRestControllerTest {
#Autowired
MockMvc mockMvc;
#Test
#Transactional
public void getAllUniversitiesResponseBodyTest() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/appln-restapis/rest/public/common/getalluniversities"))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.jsonPath("$.data")
.value(iterableWithSize(2)))
.andExpect(MockMvcResultMatchers.jsonPath("$.data[0].id", is(1)))
.andExpect(MockMvcResultMatchers.jsonPath("$.data[0].universityname", not(emptyArray())))
.andExpect(MockMvcResultMatchers.jsonPath("$.data[1].id", greaterThan(1)))
.andExpect(MockMvcResultMatchers.jsonPath("$.data[1].universityname", not(emptyArray())));
}
}
And also your test will rollback as this is not really external http call and #Transactional do work in this scenario.
Related
beginner here. I have created an API in Spring Boot using java and I want to test whether the GET request returns a 200. I'm not interested in checking what's returned (a JSON object) I just want to check whether this connection works. I have tried this code so far:
#Test
void getRequest() throws Exception {
// this doesn't work because a ResultMatcher is needed
this.mvc.perform(get("/currencies")).andExpect(HttpStatus.ACCEPTED);
//tried this too and I get "Cannot resolve method 'assertThat(int, int)'"
RequestBuilder request = get("/currencies");
MvcResult result = mvc.perform(request).andReturn();
assertThat(result.getResponse().getStatus(), 200);
}
On the first statement, I'm literally saying "do a get request for this base url and expect a http accepted status" it isn't liking it. Am I asking for too much?
my second attempt I'm saying "Make a MvcResult object and store the result of the GET request within it. Then compare it to the status code of 200"
Here is the full class
import com.example.CoinAPI.controller.CoinController;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpStatus;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.RequestBuilder;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
#AutoConfigureMockMvc
#SpringBootTest
class CoinApiApplicationTests {
#Autowired
private CoinController controller;
#Autowired
private MockMvc mvc;
#Test
void contextLoads() {
assertThat(controller).isNotNull();
}
#Test
void getRequest() throws Exception {
// this doesn't work
this.mvc.perform(get("/currencies")).andExpect(HttpStatus.ACCEPTED);
//tried this too
RequestBuilder request = get("/currencies");
MvcResult result = mvc.perform(request).andReturn();
assertThat(result.getResponse().getStatus(), 200);
}
}
How do I make this work? I have searched endlessly around Google and youtube. nothing is helping me. I am missing something, I am sure
I need to call another microservice already registered in Consul. But I can't use the Micronaut HTTP Client either the RxJava HTTP client. I need to discover the URL of that microservice to call it. Therefore, I think, I can't use #client annotation to specify the name of the service that I want to discover its URL. Please, give me an example of how can I use reactor-netty to call another microservice and could discover its URL which already registered in Consul.
I achieved this by injecting the Consul Client and calling "getInstances" function his.
package happy.shopping.apigw.infrastructure.client.rest.reports;
import io.micronaut.discovery.ServiceInstance;
import io.micronaut.discovery.consul.client.v1.ConsulClient;
import io.reactivex.Flowable;
import io.reactivex.Single;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Mono;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Random;
import java.util.stream.Collectors;
import static reactor.adapter.rxjava.RxJava2Adapter.singleToMono;
#Singleton
public class ReportClient {
private static final Logger logger
= LoggerFactory.getLogger(ReportClient.class);
#Inject
ConsulClient consulClient;
public Mono<List<ServiceInstance>> getInstances() {
Single<List<List<ServiceInstance>>> listaDeInstancias = Flowable.fromPublisher(consulClient.getInstances("ms-reports")).toList();
Single<List<ServiceInstance>> instanciasAplanadas = listaDeInstancias.map(this::flattenListOfListsStream);
return singleToMono(instanciasAplanadas);
}
public <T> List<T> flattenListOfListsStream(#NotNull List<List<T>> list) {
return list.stream()
.flatMap(Collection::stream)
.collect(Collectors.toList());
}
public Mono<Optional<String>> chooseAnInstance(){
Mono<List<ServiceInstance>> instances = getInstances();
Random rnd = new Random();
return instances.map(listOfInstances -> {
if (listOfInstances.size() > 0) {
int instanceIndex = rnd.nextInt(listOfInstances.size());
return Optional.ofNullable(listOfInstances.get(instanceIndex).getURI().toString());
}else {
return Optional.empty();
}
});
}
}
function "chooseAnInstance" returns an URL of one of the instances and I use that to call the other microservice' API. Function "chooseAnInstance" chooses one random instance.
I have a class for parsing a json response given from an API request, that was made using the restlet framework.
The method responsible for reading the json takes an object from this framework, a Representation,
public QueryResponse readResponse(Representation repr), and I would like to test the functionality of this
My question is, how do I pass a valid Representation object into this method in my JUnit test, considering I do not know how it is constructed from the API call, will I have to implement the call itself within the test to retrieve a workable object or is there another method?
For a unit test, use a mocking framework like mockito:
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.restlet.representation.Representation;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.when;
#ExtendWith(MockitoExtension.class)
public class RestletTest {
#Mock
private Representation representation;
#Test
public void demonstrateMock() {
when(representation.getAvailableSize()).thenReturn(1024l);
ClassToTest t = new ClassToTest();
assertThat(t.callRepresentation(representation), Matchers.is(1024l));
}
}
class ClassToTest {
public long callRepresentation(Representation representation) {
return representation.getAvailableSize();
}
}
I am writing JUnit test cases for my spring application. I use codepro tool in eclipse for generate test cases. when I run this test cases than it is run on JVM not on Tomcat server. so I want to know how it could be run on server? and which is best practice to run test cases on JVM or tomcat? and why? so please suggest me. code is as follow.
import java.io.InputStream;
import java.util.Properties;
import javax.servlet.http.HttpSession;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.bind.annotation.RequestMapping;
import com.zodiacapi.framework.business.ZodiacMobileBusinessTx;
import com.zodiacapi.framework.controller.ZodiacMobileAPIController;
import com.zodiacapi.framework.delegate.SendNotificationDelegate;
import com.zodiacapi.framework.dto.ReturnAPIMessageDTO;
import com.zodiacapi.framework.dto.UserDTO;
import com.zodiacweb.framework.cache.CacheService;
import com.zodiacweb.framework.cache.EhCacheServiceImpl;
import com.zodiacweb.framework.exception.ZodiacWebException;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "classpath:applicationContext.xml" })
public class ZodiacMobileAPIControllerTest extends TestCase {
private static final Logger logger = LoggerFactory.getLogger(ZodiacMobileAPIControllerTest.class);
#Autowired
private ZodiacMobileBusinessTx zodiabMobileBusinessTx;
public ZodiacMobileBusinessTx getZodiabMobileBusinessTx() {
return zodiabMobileBusinessTx;
}
#Test
public void testMobileLogin_1()
throws Exception {
ReturnAPIMessageDTO entities = new ReturnAPIMessageDTO();
Properties prop = new Properties();
InputStream in = getClass().getResourceAsStream("login.properties");
prop.load(in);
try{
UserDTO result = zodiabMobileBusinessTx.login(prop.getProperty("username"), prop.getProperty("password"), prop.getProperty("apikey"), prop.getProperty("deviceid"), prop.getProperty("deviceModel"));
System.out.println("result of test"+result);
} catch (ZodiacWebException e) {
logger.error("Internal Server Error fetching user info", e);
entities.setStatus("false");
entities.setMessage(e.getMessage());
entities.setVersion("");
} catch (Throwable t) {
entities.setStatus("false");
entities.setMessage(t.getMessage());
entities.setVersion("");
}
}
}
For a unit test you would usually execute it within the JVM. You would probably only execute Integration/Functional tests on an application running in a server.
The choices you have for testing a Spring Controller(That I am familiar with) are:
Test the controller as a regular POJO outside of the container and server
for example : MyController controller = new MyController())
Test the controller using Spring Test MVC. This will actually start up Spring during your tests.(I prefer this option) see Unit Test Spring Controllers for some examples.
If you want to test your application in a real tomcat instance you can use
Arquillian together with The Arquillian Spring Extension. This last option is definitely the most complex in terms of learning curve. But it's nice to be aware of.(Haven't successfully used it with a Spring Application myself)
Don't worry about using Arquillian for now ... it takes some time to learn.
See my code below for a working example of testing a spring controller. I noticed from your code sample that you did not have all the correct annotations and the initialization method.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = App.class)
#TestPropertySource(locations = "classpath:test.properties")
#WebAppConfiguration
public class AdminUserControllerUnitTest {
MockMvc mvc;
#Autowired
WebApplicationContext webApplicationContext;
#Before
public void initialize(){
mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
public void testListUsers() throws Exception {
Account account = new Account();
account.setId(1l);
mvc.perform(
get("/admin/user")
.sessionAttr("account",account)
);
.andExpect(MockMvcResultMatchers.model().attribute("users",hasSize(4)));
}
I'm reading this documentation on PowerMockito and it has two main examples:
Mocking static methods
Partially mocking a class
but I want to know how to mock an entire class that's created with new. I am looking for the PowerMockito version of Mockito's mock method. This should be able to replace new Foo() in my production code with a Mockito mock(Foo.class), somehow. Here's what I've tried:
import com.PowerMockitoProduction;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.mockito.Matchers.any;
import static org.powermock.api.mockito.PowerMockito.when;
#RunWith(PowerMockRunner.class)
#PrepareForTest(PowerMockitoProduction.class)
public class PowerMockitoTest {
#Test(expected = UnsupportedOperationException.class)
public void test() throws Exception {
HttpClient mock = PowerMockito.mock(HttpClient.class);
when(mock.executeMethod(any(HttpMethod.class))).thenThrow(UnsupportedOperationException.class);
new PowerMockitoProduction().createClient();
}
}
This test fails with this:
java.lang.Exception: Unexpected exception, expected<java.lang.UnsupportedOperationException> but was<java.lang.IllegalArgumentException>
Here's my production code:
package com;
import org.apache.commons.httpclient.HttpClient;
import java.io.IOException;
public class PowerMockitoProduction {
public void createClient() throws IOException {
HttpClient client = new HttpClient();
client.executeMethod(null);
System.out.println(client);
}
}
With my debugger, I can see that the client is not a mock, like I expected.
I've also tried using:
Object mock = PowerMockito.whenNew(HttpClient.class).withNoArguments().getMock();
But for some reason, that returns a mock that is not completely constructed. I've also tried this:
HttpClient mock = PowerMockito.whenNew(HttpClient.class).withNoArguments().thenReturn(mock(HttpClient.class)).getMock();
But that gives me a ClassCastException on that line. So, what is the correct way to mock out a class completely with PowerMockito?
Unlike this example implies, the reason I'm trying to mock out HttpClient is so that I can call verify it later.
You don't need to call getMock() method to get back the mocked object. Basically, mock an instance of HttpClient, store it in local variable and use that:
#Test(expected=UnsupportedOperationException.class)
public void test() {
HttpClient httpClient = mock(HttpClient.class);
PowerMockito.whenNew(HttpClient.class).withNoArguments().thenReturn(httpClient);
when(httpClient.executeMethod(any(HttpMethod.class))).thenThrow(UnsupportedOperationException.class);
new PowerMockitoProduction().createClient();
verify(httpClient).executeMethod(null);
}