import com.ssctech.eventmsg.app.model.EstimatedCash;
import com.ssctech.eventmsg.app.properties.KongAPIProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import static org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction.clientRegistrationId;
public class KongAPIService {
public static final String KONG_REGISTRATION_ID = "kong";
#Autowired
#Qualifier("kongApi")
private WebClient webClient;
#Autowired
private KongAPIProperties kongAPIProperties;
public EstimatedCash getEstimatedCash(String fundSponsorId, String positionId, String transId1, String transId2, String system) {
try {
return webClient.get()
.uri(kongAPIProperties.getCashAvailabilityUri(), positionId, transId1, transId2)
.attributes(clientRegistrationId(KONG_REGISTRATION_ID))
.header("authorizationContext", "operator=" + kongAPIProperties.getAuthorizationContext())
.header("fundSponsorId", fundSponsorId)
.header("securityChannel", kongAPIProperties.getSecurityChannel())
.header("system", system)
.header("tenant", kongAPIProperties.getTenant())
.retrieve()
.bodyToMono(EstimatedCash.class)
.block();
} catch(Exception e) {
log.error("Cannot get Cash Availability Info from API for " + "transId1 = " + transId1 + " / " + "transId2 = " + transId2, e);
return new EstimatedCash();
}
}
}
clientRegistrationId(KONG_REGISTRATION_ID) this is static method i am not able to write junit test for this method how to mock it without using PowerMock by using mockito only.
package com.ssctech.eventmsg.app.service;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.when;
import static org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction.clientRegistrationId;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatchers;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.web.reactive.function.client.WebClient;
import org.mockito.Matchers;
import com.ssctech.eventmsg.app.model.EstimatedCash;
import com.ssctech.eventmsg.app.properties.KongAPIProperties;
import reactor.core.publisher.Mono;
#RunWith(MockitoJUnitRunner.class)
public class KongAPIServiceTest {
#InjectMocks
KongAPIService KongApiService;
#Mock
WebClient webClient;
#Mock
WebClient.RequestBodyUriSpec requestBodyUriSpec;
#Mock
WebClient.RequestHeadersUriSpec requestHeadersUriSpec;
#Mock
WebClient.RequestHeadersSpec requestHeadersSpec;
#Mock
WebClient.RequestBodySpec requestBodySpec;
#Mock
WebClient.ResponseSpec responseSpec;
#Mock
EstimatedCash estimate;
#Mock
Mono mono;
#Mock
Consumer<Map<String, Object>> consumer;
private KongAPIProperties kongAPIProperties=new KongAPIProperties();
Map<String, Object> mp = new HashMap<>();
#Before
public void setup() {
KongApiService = new KongAPIService();
MockitoAnnotations.initMocks(this);
}
#Test
public void postTest() throws Exception {
when(webClient.get()).thenReturn(requestHeadersUriSpec);
kongAPIProperties.setCashAvailabilityUri("available");
when(requestHeadersUriSpec.uri(Matchers.any(String.class), Matchers.any(String.class), Matchers.any(String.class),
Matchers.any(String.class))).thenReturn(requestHeadersUriSpec);
// when(requestHeadersSpec.attributes(consumer)).thenReturn(requestBodySpec);
// when(requestHeadersSpec.header(Matchers.any(String.class), Matchers.any(String.class)))
// .thenReturn(requestHeadersSpec);
// when(requestHeadersSpec.retrieve()).thenReturn(responseSpec);
// when(responseSpec.bodyToMono(Matchers.any(Class.class))).thenReturn(mono);
// when(mono.block()).thenReturn(new String());
assertNotNull(KongApiService.getEstimatedCash("001", "1", "id1", "id2", "mfa"));
}
}
It is impossible to mock static methods using mockito only.
There's little value in mocking the entire WebClient interaction as you end up mocking everything and literally with a copy of your implementation. That's brittle and won't help when refactoring your code.
When testing a class that interacts with an HTTP Client I would recommend spawning a local HTTP server and mock the HTTP response. The MockWebServer works perfectly for this use case or WireMock/MockServer.
Most of these local mock servers allow to retrieve the request afterward so that you can inspect if all headers are present.
For the static method access, you can use Mockito to mock the method (if needed):
try (MockedStatic<ServerOAuth2AuthorizedClientExchangeFilterFunction> mockedStatic = Mockito.mockStatic(ServerOAuth2AuthorizedClientExchangeFilterFunction.class)) {
mockedStatic
.when(() -> ServerOAuth2AuthorizedClientExchangeFilterFunction.clientRegistrationId(eq("KONG_REGISTRATION_ID")))
.thenReturn("YOUR_ID");
// ...
}
If you're still planning to write a test and mock everything, consider deep stubs to avoid a lot of boilerplate code inside your test.
I had the same exact problem. I found a work around.
Use Mockito.notNull() like this:
when(requestBodyMock.attributes(notNull())).thenReturn(requestBodyMock);
I wasn't able to use the deep stubbing on this, it wouldn't work.
Hope this helps others.
Related
I am writing tests and I was looking to mock the result of the kafka admin client, when a topic is created.
I am using Mockito to write my unit tests.
Here is the test code:
import java.util.ArrayList;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.ExecutionException;
import org.apache.kafka.clients.admin.Admin;
import org.apache.kafka.clients.admin.CreateTopicsResult;
import org.apache.kafka.clients.admin.ListTopicsResult;
import org.apache.kafka.common.KafkaFuture;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.Mock;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.junit.main.Launch;
import io.restassured.RestAssured;
import io.restassured.filter.log.RequestLoggingFilter;
import io.restassured.filter.log.ResponseLoggingFilter;
#QuarkusTest
public class AppTest {
#InjectMocks
private App mockApp;
#Mock
private Client mockClient;
#Mock
private Database mockDatabase;
#Mock
private Admin mockKafkaAdmin;
#Mock
private ListTopicsResult mockListTopicResult;
#Mock
private KafkaFuture<Set<String>> mockKafkaFuture;
#Mock
private KafkaFuture<Void> mockKafkaFutureVoid;
#Mock
private Set<String> mockSet;
#Mock
private CreateTopicsResult mockCreateTopicResult;
#Mock
private Map<String, KafkaFuture<Void>> mockKafkaTopicResult;
#BeforeAll
public static void setupAll() {
RestAssured.filters(new RequestLoggingFilter(), new ResponseLoggingFilter());
}
#BeforeEach
public void setUp() {
MockitoAnnotations.openMocks(this);
}
#Test
#Launch(value = {}, exitCode = 1)
public void testLaunchCommandFailed() {}
#Test
public void testCreateTopic() throws InterruptedException, ExecutionException {
Mockito.when(mockClient.getKafka()).thenReturn(mockKafkaAdmin);
Mockito.when(mockClient.getKafka().createTopics(Mockito.anyList())).thenReturn(mockCreateTopicResult);
Mockito.when(mockCreateTopicResult.values()).thenReturn(mockKafkaTopicResult);
Mockito.when(mockKafkaTopicResult.get("meme.transmit.test")).thenReturn(mockKafkaFutureVoid);
mockApp.createTopic("test");
Mockito.when(mockClient.getKafka().listTopics()).thenReturn(mockListTopicResult);
Mockito.when(mockClient.getKafka().listTopics().names()).thenReturn(mockKafkaFuture);
Mockito.when(mockClient.getKafka().listTopics().names().get()).thenReturn(mockSet);
Assertions.assertTrue(mockApp.containsTopic("test"));
}
// ...
}
I get a nullpointer error when this line is called in the production code:
Mockito.when(mockCreateTopicResult.values()).thenReturn(mockKafkaTopicResult);
But as you can see I mocked it with mockKafkaTopicResult. What might I be missing here? Also is there an easier method to work with KafkaAdminClient when writing unit tests?
Mockito.when(mockClient.getKafka().createTopics(Mockito.anyList()))
.thenReturn(mockCreateTopicResult);
The issue was that I was using anyList(), when the createTopics() only accepts Collections.
The fix: Mockito.anyCollections()
I have a RestController that I want to test:
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class PetController implements PetApi {
#Autowired
PetRepository pr;
#Override
public ResponseEntity<Pet> addPet(#Valid Pet pet) {
pr.save(new PetBE(9L, "dummy"));
return new ResponseEntity<Pet>(pet, HttpStatus.OK);
}
}
import org.springframework.data.repository.CrudRepository;
public interface PetRepository extends CrudRepository<PetBE, Long> {
}
I want to mock PetRepository and test if the object passed is the object returned:
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import com.example.petstore.backend.api.model.Pet;
import com.example.petstore.backend.db.PetRepository;
import com.example.petstore.backend.db.PetBE;
import static org.mockito.Mockito.when;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.AdditionalAnswers.returnsFirstArg;
#SpringBootTest
public class PetControllerTest {
#InjectMocks
private PetController petController;
#MockBean
private PetRepository pr;
#Test
void testAddPet() {
when(pr.save(any(PetBE.class))).then(returnsFirstArg());
Pet p1 = new Pet().id(5L).name("Klaus");
assertNotNull(petController);
/*L35*/ ResponseEntity<Pet> r = petController.addPet(p1);
assertEquals(new ResponseEntity<Pet>(p1, HttpStatus.OK), r);
}
}
When I run this method as a gradle test, I get
com.example.petstore.backend.api.implementation.PetControllerTest > testAddPet() FAILED
java.lang.NullPointerException at PetControllerTest.java:35
which is petController.addPet(p1);.
My printlns in addPet are not displayed and I can't set any breakpoints there because it is mocked. The only reference in addPet that could be null is pr, but it works fine when I send a request with curl.
I've also tried adding
#BeforeAll
public void setup() {
MockitoAnnotations.initMocks(this);
}
because it was suggested here but that gave an InitializationException:
com.example.petstore.backend.api.implementation.PetControllerTest > initializationError FAILED
org.junit.platform.commons.JUnitException at LifecycleMethodUtils.java:57
How can I debug this?
How can I get this to work?
You're mixing annotations from various testing frameworks here. If you wish to use the Mockito annotation #InjectMocks then I'd recommend not using any Spring-related mocking annotations at all, but rather the #Mock annotation to create a mocked version of the bean you want to inject (into the #InjectMocks-annotated field). Also make sure you bootstrap the Mockito extension with #ExtendWith(MockitoExtension.class). Something like:
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import com.example.petstore.backend.api.model.Pet;
import com.example.petstore.backend.db.PetRepository;
import com.example.petstore.backend.db.PetBE;
import static org.mockito.Mockito.when;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.AdditionalAnswers.returnsFirstArg;
#ExtendWith(MockitoExtension.class)
public class PetControllerTest {
#InjectMocks
private PetController petController;
#Mock
private PetRepository pr;
#Test
void testAddPet() {
when(pr.save(any(PetBE.class))).then(returnsFirstArg());
Pet p1 = new Pet().id(5L).name("Klaus");
assertNotNull(petController);
ResponseEntity<Pet> r = petController.addPet(p1);
assertEquals(new ResponseEntity<Pet>(p1, HttpStatus.OK), r);
}
}
EDIT: Calling MockitoAnnotations.initMocks(this) inside for example a #BeforeEach-annotated method is necessary if you don't want to use the MockitoExtension. They're essentially the same thing, but it's less necessary in JUnit Jupiter because you can extend a test class with multiple extensions, which was not possible in JUnit 4.x. So if you wanted to bootstrap your test with both a Spring context and Mockito, then you had to pick one of them and setup the other one yourself.
I am currently writing some unit tests for legacy code in a rest api application. One particular api call generates a list of URIs using the UriInfo api from Jax-RS. When I mock this interface, I am able to get the tests to run, but each call to the interface appears to reuse the same URI object and appends to it over and over, rather then a new URI object being passed to the tested code. I have a sample class with test that demonstates the issue:
import java.net.URI;
import java.util.LinkedList;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;
#Stateless
#Path("/")
public class MockitoUrl {
#PersistenceContext(name = "MockitoTestPU")
private EntityManager em;
#Context
private UriInfo uriInfo;
#Path("system")
#GET
#Produces("text")
public List<URI> generateUris() {
List<URI> uris = new LinkedList<>();
for (int i=1;i<5;i++) {
final URI uri = uriInfo.getBaseUriBuilder().path(
MockitoUrl.class, "generateUris").build("test");
System.out.println(uri.toString());
}
return uris;
}
}
The unit test:
import java.net.URI;
import java.util.List;
import javax.persistence.EntityManager;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import static org.mockito.Mockito.when;
import org.mockito.MockitoAnnotations;
public class MockitoUrlTest {
#Mock
private EntityManager em;
#Mock
private UriInfo uri;
#InjectMocks
MockitoUrl mockitoUrl;
public MockitoUrlTest() {
}
#Before
public void setUp() {
mockitoUrl = new MockitoUrl();
MockitoAnnotations.initMocks(this);
}
#Test
public void testGenerateUris() throws Exception {
System.out.println("generateUris");
String baseUri = "http://server1/api";
when(uri.getBaseUriBuilder()).thenReturn(UriBuilder.fromUri(baseUri));
List<URI>result = mockitoUrl.generateUris();
for (URI u : result) {
assertTrue(u instanceof URI);
System.out.println(u.toString());
}
}
}
When the test is run, the output can be seen thus:
generateUris
http://server1/api/system
http://server1/api/system/system
http://server1/api/system/system/system
http://server1/api/system/system/system/system
What I would have expected is that it would return the same URI each time. In the real application these URIs would have more detail and each would have different data from a JPA class lookup. It seems that the same object is returned each time and then appended to again.
Is there a way to get Mockito to return a fresh URI each time uriInfo.getBaseUriBuilder() is called, or is this a fundamental misunderstanding I have about how Mockito works?
The problem you face is expected behavior. When setting up the mock you create a single instance and that is always returned.
To get what you want I would suggest using when(..).thenAnswer(i -> UriBuilder.fromUri(baseUri))
By this every call executes the provides lambda and it creates a new instance per invocation.
I have a question about the use of Mockito in Java.
I will attach the 2 blocks of similar code:
The first one, which works great
package controller;
import model.DatabaseModel;
import org.json.JSONArray;
import org.json.JSONObject;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;
public class TestController {
JSONObject jsonObj1;
JSONArray testArr;
#Mock
private DatabaseModel testDB;
#InjectMocks
private Controller controller;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
jsonObj1 = new JSONObject();
jsonObj1.put("name", "Bar");
testArr = new JSONArray();
testArr.put(jsonObj1);
when(testDB.getActivites()).thenReturn(testArr);
}
#Test
public void testServerCon() {
ArrayList<String> testServer = new ArrayList<>();
testServer.add("Bar");
assertEquals(testServer, controller.getAllActivites());
}
}
And the second one, which doesn't really works:
package controller;
import model.DatabaseModel;
import org.json.JSONArray;
import org.json.JSONObject;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.*;
public class TestController2 {
JSONObject jsonObj1;
JSONArray testArr;
DatabaseModel testDB;
Controller controller;
#Before
public void setup() {
jsonObj1=new JSONObject();
jsonObj1.put("name", "Bar");
testArr=new JSONArray();
testArr.put(jsonObj1);
controller=new Controller();
testDB=mock(DatabaseModel.class);
when(testDB.getActivites()).thenReturn(testArr);
verify(testDB).getActivites();
}
#Test
public void testServerCon(){
ArrayList<String> testServer=new ArrayList<>();
testServer.add("Bar");
assertEquals(testServer, controller.getAllActivites());
}
}
When I used the verify method for the second one, I get an error that the method never got called on the mock, So I wonder what am I missing?
I have looked up tutorials online, some use annotations and some use the method calls, but if I am not mistaken they should be the same, So what am I missing here?
In your first example the #InjectMocks annotation causes the instance of Controller to be injected with the mocked DatabaseModel.
In your second example, you construct the Controller like so ...
controller=new Controller();
... but you never inject the mocked Database Model into this instance of the Controller. So, when you invoke controller.getAllActivites() your controller never uses the mocked DatabaseModel. You can achieve the same outcome as that in the first example if you can inject the DatabaseModel into your Controller. For example:
testDB=mock(DatabaseModel.class);
// constructor injection
controller=new Controller(testDB);
// setter injection
controller=new Controller();
controller.setDatabaseModel(testDB);
Note: even if you remove verify(testDB).getActivites() from the setup method the test will fail its assertion unless you actualy provide the mocked DatabaseModel to the controller.
You are doing a verify in the setup method in the second version...
#Before
public void setup() {
jsonObj1=new JSONObject();
jsonObj1.put("name", "Bar");
testArr=new JSONArray();
testArr.put(jsonObj1);
controller=new Controller();
testDB=mock(DatabaseModel.class);
when(testDB.getActivites()).thenReturn(testArr);
verify(testDB).getActivites(); // REMOVE THIS LINE
}
Nothing would have been called by this time, hence the error.
You probably want to move this line into your test method, for example...
#Test
public void testServerCon(){
ArrayList<String> testServer=new ArrayList<>();
testServer.add("Bar");
assertEquals(testServer, controller.getAllActivites());
verify(testDB).getActivites();
}
I referred to all available resources on Stackoverflow for similar queries. But I'm not sure what the issue with this test is:
It is throwing the following exception.
[main] ERROR com.example.dao.spring.TransactionDAOSpring - org.mockito.exceptions.misusing.InvalidUseOfMatchersException: Invalid use of argument matchers!0 matchers expected, 2 recorded.
Following is the code:
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyMapOf;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.simple.SimpleJdbcCall;
import com.example.dto.DisplayTwo;
import com.example.dto.DisplayOne;
import com.example.dto.DisplayThree;
public class TransactionDAOSpringTest {
TransactionDAOSpring transactionDAOSpring;
#Mock
DataSource dataSource;
#Mock
JdbcTemplate jdbcTemplate;
#Mock
SimpleJdbcCall storedProc;
#Rule
public ExpectedException thrown = ExpectedException.none();
private Map<String, Object> resultMap;
private List<DisplayOne> displayOne;
private List<DisplayTwo> displayTwo;
private List<DisplayThree> displayThree;
#Before
public void beforeMethod() {
MockitoAnnotations.initMocks(this);
transactionDAOSpring = new TransactionDAOSpring();
transactionDAOSpring.setJdbcTemplate(jdbcTemplate);
transactionDAOSpring.setDataSource(dataSource);
transactionDAOSpring.retrieveResultStoredProc = storedProc;
resultMap = new HashMap<String, Object>();
displayOne = new ArrayList<DisplayOne>();
displayTwo = new ArrayList<DisplayTwo>();
displayThree = new ArrayList<DisplayThree>();
}
#Test
public void testRetrieve_When_ResultSet_Not_Empty() {
displayOne.add(new DisplayOne());
displayTwo.add(new DisplayTwo());
displayThree.add(new DisplayThree());
resultMap.put("DisplayOneResultSet", displayOne);
resultMap.put("DisplayTwoResultSet", displayTwo);
resultMap.put("DisplayThreeResultSet", displayThree);
when(storedProc.execute(anyMapOf(String.class, Object.class)))
.thenReturn(resultMap);
Map<String, Object> returnedResultMap = transactionDAOSpring.retrieve(anyString(),
anyLong());
assertEquals(resultMap.size(), returnedResultMap.size());
}
Update: After debugging, it looks like it fails to getConnection from the dataSource and hence throws the exception.
Any help would be appreciated.
Matchers like anyString() or anyLong() can be used for mocking an object, for example inside when() or verify() invocation. In your case:
Map<String, Object> returnedResultMap = transactionDAOSpring.retrieve(
anyString(), anyLong());
is real method call. I think that is what causes InvalidUseOfMatchersException. Try invoking your method with stub values like empty string and 0L