How can I use MockWebServer for Junit test cases of webclient? - unit-testing

I have a spring-boot application which calls some third party URL (let's say http://example.com/someUri) using webclient(I have used application-dev.properties for injecting this url in my application to achieve loose coupling) and consumes the response and use it in my application.
It's my first time when I am going to write test cases for webclient. and there I used #SprintBootTest.
I found that there are two ways where I can test my webclient with third party Api call by mocking the api call and make it call to my local url(which will be using url(http://localhost:{portNumber}/someUri) from my testing properties file: src/test/resources/application.properties) where It will be giving some mockedResponse in return to my real client:
Using wiremock
Using MockWebServer
consider above code for better understanding:
#Service
Class SampleService{
#Value("${sample.url}")
private String sampleUrl;
public String dummyClient() {
String sample =webClient.get()
.uri(sampleUrl)
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.retrieve()
.bodyToMono(String.class)
.block();
return sample;
}
}
application-dev.properties:
sample.url:http://example.com/someUri
src/test/resouces/application.properties:
http://localhost:8090/someUri
Testing class:
#SpringBootTest
public class sampleTestingClass {
#Autowired
private SampleService sampleService;
#Value("${sample.url}")
private String sampleUrl;
public static MockWebServer mockWebServer = new MockWebServer();
#BeforeAll
static void setUp() throws IOException {
mockWebServer.start(8090);
}
#AfterAll
static void tearUp() throws IOException {
mockWebServer.close();
}
HttpUrl url = mockWebServer.url("/someUri");
mockWebServer
.enqueue(
new MockResponse()
.setResponseCode(200)
.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.setBody("Sample Successful"));
String sample = sampleService.dummyClient();
assertEquals(sample ,matches("Sample Successful"));
}
}
but this code isn't working. it's giving me above error:
java.lang.NullPointerException
It will be really helpful if anybody knows how this can be fixed to achieve my unit testing using mocked Url? Thanks in advance!

Here is a working example:
#Component
public class QuotesClient {
private final WebClient webClient;
public QuotesClient(WebClient.Builder builder, #Value("${client.baseUrl}") String baseUrl) {
this.webClient = builder.baseUrl(baseUrl).build();
}
public JsonNode getData() {
return this.webClient
.get()
.retrieve()
.bodyToMono(JsonNode.class)
.block();
}
}
Using the WebClient.Builder is optional.
The corresponding test can look like the following:
class QuotesClientTest {
private QuotesClient quotesClient;
private MockWebServer server;
#BeforeEach
public void setup() {
this.server = new MockWebServer();
this.quotesClient = new QuotesClient(WebClient.builder(), server.url("/").toString());
}
#Test
public void test() {
server.enqueue(new MockResponse()
.setStatus("HTTP/1.1 200")
.setBody("{\"bar\":\"barbar\",\"foo\":\"foofoo\"}")
.addHeader("Content-Type", "application/json"));
JsonNode data = quotesClient.getData();
assertNotNull(data);
System.out.println(data);
}
}
If you are searching for a similar setup using WireMock, Spring Boot, and JUnit 5, take a look at the linked guide.

Related

***HELP*** Test/Mock: Service Class using Rest Template, Response and HTTP Entities, Exceptions

I have a service class, with for readability purpose, I have provided the code with dummy variables and objects. I am trying to write a JUNIT test class for the service, primarily with Mockito. No matter how hard I try, I am not able to hit the method serviceMethod irrespective of using spy/mock. I have also included a test, following the main class.
I know I am missing something here, but doesn't cross my mind. I need an eye to review this and let me know how I can write a proper test class for this and obtain coverage for the method.
(P.S. all the necessary imports are in-place and not pasted here to keep this concise)
Thanks in advance!
#Service
public class ServiceClass {
private static final Logger LOGGER = LoggerFactory.getLogger(ServiceClass.class);
#Autowired
String stringUrl;
RestTemplate restTemplate = new RestTemplate();
public void serviceMethod(ModelObject model) {
try {
HttpEntity<ModelObject> request = new HttpEntity<>(model);
ResponseEntity<String> response = restTemplate.exchange(stringUrl,
HttpMethod.POST, request, String.class);
LOGGER.info(response.getBody() + "and " + response.getStatusCode());
} catch (HttpClientErrorException exception) {
LOGGER.info(exception.getResponseBodyAsString());
} catch (HttpStatusCodeException exception) {
LOGGER.info(exception.getResponseBodyAsString());
}
}
Sample Test:
#RunWith(MockitoJUnitRunner.Silent.class)
public class ServiceClassTest {
#InjectMocks
private ServiceClass serviceClass;
#Mock
private RestTemplate restTemplate;
#Test
public void testServiceMethod() {
ServiceClass spy = Mockito.spy(serviceClass);
// ServiceClass spy = mock(ServiceClass.class, Mockito.CALLS_REAL_METHODS);
doNothing().when(spy).serviceMethod(Mockito.any(ModelObject.class));
Mockito.doCallRealMethod().when(spy).serviceMethod(Mockito.any(ModelObject.class));
ResponseEntity<String> responseEntity = new ResponseEntity<>(HttpStatus.ACCEPTED);
Mockito.when(restTemplate.exchange(ArgumentMatchers.anyString(), ArgumentMatchers.any(HttpMethod.class),
ArgumentMatchers.<HttpEntity<ModelObject>>any(), ArgumentMatchers.<Class<String>>any()))
.thenReturn(responseEntity);
}

How to test POST method in Spring boot using Mockito and JUnit

I am newbie to Unit testing using JUnit and Mockito in Spring boot framework.
I want to test this method. How to test POST Request method:
// add Employee
#RequestMapping(method = RequestMethod.POST)
public void addEmployee(#RequestBody Employee employee){
this.employeeService.addEmployee(employee);
}
Thank you in advance
As #merve-sahin rightly pointed out, you can use #WebMvcTest to achieve this.
Look at the following example :
#RunWith(SpringRunner.class)
#WebMvcTest(YourController.class)
public class YourControllerTest {
#Autowired MockMvc mvc;
#MockBean EmployeeService employeeService;
#Test
public void addEmployeeTest() throws Exception {
Employee emp = createEmployee();
mvc.perform(post("/api/employee")
.contentType(MediaType.APPLICATION_JSON)
.content(toJson(emp)))
.andExpect(status().isOk());
}
}
In Above code you can mock your dependent service using #MockBean.
The test will perform post on your custom Employee object and validate the response
You can add headers, authorization while calling perform
Assuming you using JSON as media type, you can write toJson() method using any json library to convert Employee object into Json string format
private String toJson(Employee emp) {
If you are using XML, then you can do the same for XML
You can validate the response using expectations in chained way.
As rightly pointed out, please checkout MockedMvc link which should help you
Go through this following example:
#RunWith(SpringJUnit4ClassRunner.class)
public class ApplicationControllerTest {
#Mock
EmployeeService employeeService;
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
initMocks(this);
YourController controller = new YourController(employeeService);
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test
public void addEmployee() throws Exception {
Employee emp = new Employee("emp_id","emp_name");//whichever data your entity class have
Mockito.when(employeeService.addEmployee(Mockito.any(Employee.class))).thenReturn(emp);
mockMvc.perform(MockMvcRequestBuilders.post("/employees")
.content(asJsonString(emp))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().contentType("application/json;charset=UTF-8"));
}
public static String asJsonString(final Object obj) {
try {
return new ObjectMapper().writeValueAsString(obj);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
In above given example mock your service class which is required to post the data to your Employee entity class.
I'm assuming that you're doing this via controller so you first need to initialize the controller which comes under the #Before annotation.
By doing above example you'll be able to post your data into the JSON format.
The below example is using JUnit5, Mockito3.x, spring-boot2.4.4, and
assertj3.x
The spring-boot-starter-test dependency from version 2.2.0
already comes with Junit 5 and contains also Hamcrest, assertj, and Mockito libraries.
In JUnit 5, “Runner” extension points, available in JUnit 4, are replaced by the Extension API.
You can register the Mockito extension via #ExtendWith.
Initializes mocks annotated with #Mock annotation so that explicit usage of MockitoAnnotations#initMocks(Object) is not required.
From spring-boot 2.1, there is no need to load the SpringExtension using annotation #ExtendWith because it's included as a meta-annotation in these annotations #DataJpaTest, #WebMvcTest, and #SpringBootTest.
Complete example with Github link: https://github.com/jdamit/DemoSpringBootApp.git
**#WebMvcTest(controllers = UserController.class)**
public class UserControllerTest {
#Autowired
private MockMvc mockMvc;
#Autowired
private ObjectMapper mapper;
#MockBean
private UserServiceImpl userService;
private List<UserDto> users;
private UserDto user;
private String URI = "/users";
#BeforeEach
void setUp(){
users = List.of(new UserDto("Amit", "Kushwaha", "jdamit2027#gmail.com", "sector 120"),
new UserDto("Amit", "Kushwaha", "jdamit2027#gmail.com", "sector 120"),
new UserDto("Amit", "Kushwaha", "jdamit2027#gmail.com", "sector 120"));
user = new UserDto("Rahul", "Swagger", "rahul.swagger#gmail.com", "sector 120");
}
#Test
//#Disabled
void getUsersTest() throws Exception {
Mockito.when(userService.getUsers()).thenReturn(users);
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get(URI)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isOk())
.andReturn();
Assertions.assertThat(result).isNotNull();
String userJson = result.getResponse().getContentAsString();
Assertions.assertThat(userJson).isEqualToIgnoringCase(mapper.writeValueAsString(users));
}
#Test
//#Disabled
void createUserTest() throws Exception {
Mockito.when(userService.createUser(Mockito.any(UserDto.class))).thenReturn(user);
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.post(URI)
.contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsString(user).getBytes(StandardCharsets.UTF_8))
.accept(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isOk())
.andReturn();
Assertions.assertThat(result).isNotNull();
String userJson = result.getResponse().getContentAsString();
Assertions.assertThat(userJson).isNotEmpty();
Assertions.assertThat(userJson).isEqualToIgnoringCase(mapper.writeValueAsString(user));
}
}

Apache Camel Beans Unit Testing

I am using Apache Camel with Spring boot in my application. Currently I am working on a Unit test.
Java Code
DataRoute class
from("direct:getData")
.routeId("getData")
.bean(DataService.class, "processData")
.marshal().json(JsonLibrary.Jackson)
.end();
DataService class
public Data processData() {
return new Data("Hello World");
}
Data Class with getters, setters and Jackson toString method
private String value;
Unit test
BaseCamelContextUnitText
public abstract class BaseCamelContextUnitTest extends CamelTestSupport
{
#Autowired
private DataService dataService;
#Produce
private ProducerTemplate producerTemplate;
public CamelContext getCamelContext() {
return camelContext;
}
#Override
protected Context createJndiContext() throws Exception {
JndiContext context = new JndiContext();
context.bind("dataService", dataService);
return context;
}
#Test
public void shouldProcessData() throws Exception {
RouteDefinition routeDef = getCamelContext().getRouteDefinition("getData");
routeDef.adviceWith((ModelCamelContext) getCamelContext(), new RouteBuilder() {
#Override
public void configure() throws Exception {
from("direct:getData")
.pipeline("bean:dataService?method=processData");
}
});
getCamelContext().start();
String responseData = "{"
+ "\"value\":\"Unit test success\""
+ "}";
Object response = producerTemplate.sendBody("direct:getData", ExchangePattern.InOut, null);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
((InputStreamCache) response).writeTo(byteArrayOutputStream);
assertThat(new String(byteArrayOutputStream.toByteArray()), is(responseData));
}
}
How do I mock
.bean(DataService.class, "processData")
in the unit test to return a mock Data Object with its default String variable as say "Unit test success" and then test to see if the route would give back the mocked Object instead of the Object with "Hello World" String variable?
This may seem's a late response, but I faced the same thing as described in your case, and I come across a simple way to "mock" the bean step, by using the DefaultRegistry to register the mocked bean into Camel registry, for example :
#Test
public void shouldProcessData() throws Exception {
...
...
DataService dataService = new DataService(...);
stubBean("dataService", dataService); // put this before calling context.start();
context.start();
...
...
}
/**
* This method inject a stub bean into the registry
* #param beanName the name of the bean being injected
* #param instance the stub instance
*/
void stubBean(String beanName, Object instance) {
DefaultRegistry registry = context.getRegistry(DefaultRegistry.class);
registry.bind(beanName, instance);
}
You can autowired the DataService in your DataRoute like
#Component
public class DataRoute extends RouteBuilder {
#Autowired
private DataService dataService;
#Override
public void configure() throws Exception {
from("direct:getData")
.routeId("getData")
.bean(dataService, "processData")
.marshal().json(JsonLibrary.Jackson)
.end();
}
}
So you can mock the DataService as usual.
An alternative is using beanRef("beanName", "methodName") in the Route.

Mockito: stub function is not working

I am using Mockito to write a simple unit test.
Then, a function under test:
public class MyService {
public void getData() {
executor.execute(new MyRunnable() {
#Override
doTask() {
MyRestClient client = getRestClient();
Response resp = client.getFromServer();
persist(resp.getData());
}
});
}
}
protected MyRestClient getRestClient() {
return new MyRestClient();
}
My test case, I want to test doTask() has run & resp.getData() is persisted:
#Test
public void testGetData() {
MyService spyService = spy(MyService.getInstance());
// mock client
MyRestClient mockedClient = mock(MyRestClient.class);
mockedClient.setData("testData");
// stub getRestClient() function to return mocked client
when(spyService.getRestClient()).thenReturn(mockedClient);
// SUT
spyService.getData();
// run the Runnable task.
Mockito.doAnswer(new Answer<Object>() {
public Object answer(InvocationOnMock invocation) throws Exception {
Object[] args = invocation.getArguments();
Runnable runnable = (Runnable) args[0];
runnable.doTask();
return null;
}
}).when(executor).execute(Mockito.any(Runnable.class));
...
}
As you see above, I stub the getRestClient() function to return a mocked MyRestClient. However when run the test case, it doesn't stub the getRestClient() but run the real function. Why?
[Edit] following comment and review feedback
A rule of thumb is not to mock the class under test. Also your testing will be much easier if your class under test does not use the new keyword. Instead use Factory classes to create objects. There will be no need to use Mockito.spy() only Mockito.mock().
The fact that the following answer requires significant test setup is telling you that MyService has too much reposibility and needs to be simplified. However for the sake of answering your question directly here is how you can refactor your code to support verifying the call to persist() using Mocks.
MyService accepts in the constructor the objects that you will be mocking in your test setup. Having them passed into the constructor allows your JUnit test case to create the Mocks and keep a reference to them for verification later.
public class MyService {
private MyRunnableFactory runFactory;
private MyRestClientFactory restFactory;
private MyRestDao dao;
// inject constructor arguments
public MyService(MyRunnableFactory runFactory, MyRestClientFactory restFactory, MyRestDao dao) {
this.runFactory = runFactory;
this.restFactory = restFactory;
this.dao = dao;
}
public void getData() {
MyRestClient restClient = restFactory.createInstance();
MyRunnable runner = runFactory.createInstance(restClient, dao);
executor.execute(runner);
}
}
MyRunnable is created so that it can be tested in isolation if required. Again we inject the Mock objects into the constructor. It is tempting to inline Runnables as you have written in your question, however you lose the ability to control the new instance being created within you tests.
public class MyRunnable implements Runnable {
private MyRestClient restClient;
private MyRestDao dao;
public MyRunnable(MyRestClient restClient, MyRestDao dao) {
this.restClient = restClient;
this.dao = dao;
}
public void run() {
Response resp = restClient.getFromServer();
dao.persist(resp.getData());
}
}
MyRestDao is created because this is the class that you want to Verify in your test case. I don't see where persist() is defined in your question so we create a Data Access Object (DAO) to implement it.
public class MyRestDao {
public void persist() {
// save to some repository
}
}
Now let's write the test case that uses the above classes. We want to verify that the persist() method has been called
#RunWith(MockitoJUnitRunner.class)
public class MyServiceTest {
#Mock MyRestDao dao;
#Mock MyRestClient restClient;
#Mock MyRunnableFactory runFactory;
#Mock MyRestClientFactory restFactory;
#Test
public void testPersistIsCalled() {
Response expectedResponse = new Response("some data"); // real implementation, not mocked
MyRunnable runner = new MyRunnable(restClient, dao); // real implementation, not mocked
when(restFactory.createInstance()).thenReturn(restClient);
when(runFactory.createInstance(restClient, dao)).thenReturn(runner);
when(restClient.getFromServer()).thenReturn(expectedResponse);
when(restClient.getData()).thenReturn(myRunnable);
// method under test
MyService service = new MyService(runFactory, restFactory);
service.getData();
verify(dao).persist(expectedResponse.getData());
}
}
Note that this test case is brittle because it is tightly coupled to the actual implementation of the MyService class. Ideally you want tests that don't need to know about the internal workings of your class under test.

gwt junit for testing objectify

I am trying to test my gwt app with gwt junit but seems to not be able to set up things correctly to make the objectify be tested.
All the tutorials demonstrate testing DataStore but not objectify (which is higher level of data base service)
My base class for testing looks like this:
public class TestBase {
private static final LocalServiceTestHelper helper =
new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig());
protected static ObjectifyFactory fact;
#BeforeClass
public static void setUp() {
helper.setUp();
fact = new ObjectifyFactory() {
#Override
public Objectify begin(ObjectifyOpts opts)
{
opts.setSessionCache(false);
return super.begin(opts);
}
};
}
#AfterClass
public static void tearDown() {
helper.tearDown();
}
}
then i have classes that extends the base:
public class UserServiceTest extends TestBase{
private User inactiveUser;
private UserService us;
Objectify _ofy;
#Rule
public ExpectedException thrown = ExpectedException.none();
#Before
public void beforeTest() {
//Register the classes used in the test
fact.register(User.class);
us = new UserService();
inactiveUser = new User();
}
#Test
public void basicTest(){
Objectify ofy = ObjectifyService.begin();
ofy.put(inactiveUser); //This fails with exception: An exception occurred: com.google.apphosting.api.ApiProxy$CallNotFoundException
//My goal is to reach these test but "addUser" uses also objectify
//UserService.addUser("shpungin#gmail.com", "bye");
//assertNotNull(inactiveUser.get_id());
}
Do you have an idea of what am I doing wrong? I looked all over the Internet and found no solution (some even said to remove app-engine-sdk from .classpath but it dosent seems to work.
Thank you.
I solved this.
Although com.google.apphosting.api.ApiProxy should be part of the app-engine
Some jars still needs to be inside the .classpath :
${SDK_ROOT}/lib/testing/appengine-testing.jar
${SDK_ROOT}/lib/impl/appengine-api.jar
${SDK_ROOT}/lib/impl/appengine-api-labs.jar
${SDK_ROOT}/lib/impl/appengine-api-stubs.jar //This one I missed
Also I upgraded my app-engine to v 1.6.4.1 (maybe that also helped).