Mockito test throwing "URI is not absolute" error - java

I am trying to mock this method with postForEntity call -
public AuthorizeClient(RestTemplateBuilder builder, Config config) {
this.grantedUrl = config.grantedUrl();
this.restTemplate = HttpClientHelper.getRestTemplate(builder, authorizationConfig);
}
private final RestTemplate restTemplate;
private String grantedUrl;
public List<Permission> getPermissions(
PermissionsRequest permissionsRequest) {
try {
var headers = new HttpHeaders();
var request = new HttpEntity<PermissionsRequest>(permissionsRequest, headers);
var permissions = restTemplate.postForEntity(grantedUrl, request, Permission[].class);
return Arrays.asList(permissions.getBody());
} catch (HttpClientErrorException err) {
logger.error(err);
throw err;
}
}
Here is my test case -
RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
Config config = new Config();
#InjectMocks
AuthorizeClient authorizeClient = new AuthorizeClient(restTemplateBuilder, config);
#Mock
private RestTemplate restTemplate;
PermissionsRequest permissionsRequest;
ResponseEntity<Permission[]> expGrantedPermissions;
#Test
public void testAuthorizationPermissions() {
when(restTemplate.postForEntity(anyString(), any(), eq(Permission[].class))).thenReturn(expGrantedPermissions);
var res = authorizeClient.getAllGrantedPermissions(permissionsRequest);
assertNotNull(res);
}
I'm getting this error. Looks like mock is not created properly ..
java.lang.IllegalArgumentException: URI is not absolute
At this line -
var res = authorizeClient.getPermissions(permissionsRequest);
My AuthorizeClient is constructed like above..
Please suggest what am I missing.
Thanks in advance

From your example code I don't see a relation between your restTemplate mock and the AuthorizeClient class.
The problem is that your restTemplate field in your object is final. In this case - even so the #InjectMocks generally works with the new constructor - injection of the mock does not happen.
You might want to add the full stacktrace, but I would assume that config.grantedUrl() does not return a valid url.
I assume grantedUrl is a String. In that case you define behaviour for the wrong method. There is only a 4-param version of postForEntity, so you'll need to define the mock for that using Mockito.<Object>any() for the varargs parameter.
To fix this: You might want to mock the RestTemplateBuilder and define behaviour for the methods that are used by HttpClientHelper.getRestTemplate.
You might also want to consider using PowerMockito to mock the static method directly.
Alternatively you could refactor your code to pass the RestTemplate directly to the constructor instead of passing the builder. At least in your example the builder does not seem to be used for anything else within the class, so I would consider it a removable dependency.
Also using Constructor injection is considered the way to go by many.
I assume PermissionsRequest andPermission` are your classes, so I can't really test this specific case, but basically this should work:
Note that I assume a changed constructor for AuthorizeClient that accepts both config and restTemplate. Instead of using the annotation I setup the mock manually, because you used a real Config object in your example. If mocking both is an option, you can still use the #InjectMocks annotation.
#RunWith(MockitoJUnitRunner.class)
public class RestTemplateTest {
Config config = new Config();
#Mock
private RestTemplate restTemplate;
PermissionsRequest permissionsRequest;
ResponseEntity<Permission[]> expGrantedPermissions;
#Test
public void testAuthorizationPermissions() {
// init permissionsRequest and expGrantedPermissions, if you haven't done that
AuthorizeClient authorizeClient = new AuthorizeClient(config, restTemplate);
Mockito.when(restTemplate.postForEntity(Mockito.anyString(), Mockito.any(), Mockito.eq(Permission[].class), Mockito.<Object>any())).thenReturn(expGrantedPermissions);
List<Permission> res = authorizeClient.getAllGrantedPermissions(permissionsRequest);
assertNotNull(res);
}
}
Ps.:
For a standalone example of a different method on restTemplate you can check my answer here. This at least can help you verify that you mock the correct method.

Related

Java Unittest Mocked Unittest Not working

I'm trying to create a unittest for the method below (myHostClient), but I'm having some problems with it:
MyClass.java
import com.abc.def.ServiceBuilder
public class MyClass {
#Value("${materialLocation}")
private String materialValue
private static final SERVICEBUILDER = new ServiceBuilder()
#Bean public MyHostServiceClient myHostClient(
#Value(${qualifier_one}) final String qualiferOne,
#Value(${use_service}) final boolean useService) {
if(useService) {
return SERVICEBUILDER
.remote(MyHostServiceClient.class)
.withConfig(qualifierOne)
.withCall(new CallerAttach(Caller.retro(defaultStrategy())), // Error Line2 Here
new SigningVisitor(new CredentialsProvider(materialValue))),
call -> call.doSomeStuff(StuffObject.getStuffInstance()))
.makeClient();
}
#Bean DefaultStrategy<Object> defaultStrategy() {
final int valueA = 1;
final int valueB = 2;
return new DoSomeThingsBuilder()
.retry(valueA)
.doSomethingElse(valueB)
.create();
}
}
And here is my latest unsuccessful attempt at writing a unittest for it:
MyClassTest.java
import org.mockito.Mock
import static org.mockito.Mockito.times
public class MyClassTest {
#Mock
private SERVICEBUILDER serviceBuilder;
private MyClass myClass;
private String qualifierOne = "pass"
#BeforeEach
void setUp() {
myClass = new MyClass();
}
#Test
public void test_myHostClient() {
boolean useService = true;
final MyHostServiceClient result = myclass.myHostClient(qualifierOne, useService); // Error Line1 here
verify(serviceBuilder, times(1));
}
}
I have been trying to mock SERVICEBUILDER and verify that the mocked object is called one time but no luck so far. Right now I'm getting this error:
IllegalArgumentException: Material Name cannot be null
And it points to these lines in my code.
In the Test:
final MyHostServiceClient result = myclass.myHostClient(qualifierOne, useService);
Which points to this line in the module:
.withCall(new CallerAttach(Caller.retro(defaultStrategy())),
Anyone know how I can fix my unittest or write a working one from scratch?
I would say the design of MyClass is quite wrong because it looks like a Spring configuration but apparently it's not. If it is really supposed to be a configuration then I wouldn't even test it like this because it would rather be an integration test. Of course, even in integration tests you can mock dependencies. But the test itself would run differently and you would have to spin up a suitable Spring context, etc.
So given the above, I would rather make MyClass some sort of MyHostServiceClientFactory with removing all of the Spring annotations and then fix the following problems in your code.
SERVICEBUILDER is hardcoded.
SERVICEBUILDER is static final and its value is hardcoded into MyClass. You will not be able to reassign that field with the mocked version. It can still be final but not static then and it's better to use dependency injection here by passing the value through the MyClass constructor.
SERVICEBUILDER will still be not mocked even if you fix the above.
To really mock SERVICEBUILDER by using the #Mock annotation in the test you should enable Mockito annotations.
If you are using JUnit5 then you should annotate your test class like this:
#ExtendWith(MockitoExtension.class)
public class MyClassTest {
...
}
If you are stuck with JUnit4 then you should use another combination:
#RunWith(MockitoJUnitRunner.class)
public class MyClassTest {
...
}
Once you've done that the SERVICEBUILDER will be mocked but now you will have to configure the behaviour of that mock, like what is going to be returned by the SERVICEBUILDER methods. I can see 4 methods in total, namely remote, withConfig, withCall, and makeClient. You will have to do Mockito's when/thenReturn configurations.
MyClass.materialValue is null.
But even when your mock will be properly configured you will still encounter the original IllegalArgumentException: Material Name cannot be null. This is because MyClass.materialValue will still be null and looks like CredentialsProvider cannot accept that. As I can see, that field is supposed to be injected by Spring using the #Value annotation, but remember this class no longer contains anything from Spring. As in problem 1, you have to pass the value through the MyClass constructor.
Once all of these problems are solved you can introduce a thin Spring configuration like MyHostServiceClientConfiguration (or whatever name suits you) that would serve as a provider of necessary properties/dependencies for MyHostServiceClientFactory (existing MyClass) and then this factory can provide you with a MyHostServiceClient bean through a method like MyHostServiceClientConfiguration#myHostServiceClient annotated with #Bean.
Conceptually your MyHostServiceClientFactory will look like this:
public class MyHostServiceClientFactory {
private final String materialValue;
private final ServiceBuilder serviceBuilder;
public MyHostServiceClientFactory(String materialValue, ServiceBuilder serviceBuilder) {
this.materialValue = materialValue;
this.serviceBuilder = serviceBuilder;
}
public MyHostServiceClient myHostClient(String qualiferOne, boolean useService) {
if(useService) {
return serviceBuilder
.remote(MyHostServiceClient.class)
.withConfig(qualifierOne)
.withCall(new CallerAttach(Caller.retro(defaultStrategy())), // Error Line2 Here
new SigningVisitor(new CredentialsProvider(materialValue))),
call -> call.doSomeStuff(StuffObject.getStuffInstance()))
.makeClient();
}
// can also be injected as a dependency rather than being hardcoded
DefaultStrategy<Object> defaultStrategy() {
final int valueA = 1;
final int valueB = 2;
return new DoSomeThingsBuilder()
.retry(valueA)
.doSomethingElse(valueB)
.create();
}
}

Mockito failing inside internal call for mocked method

I'm trying to mock the return value for a method using the when call from mockito. However, I'm new to this and I may perhaps be misunderstanding how mockito works, since the call is failing inside the method mocked when that calls another method. I thought regardless of how that method is implemented, I should be getting the return value I'm asking for? Or do I need to mock also the internals for that method? I feel that shouldn't be it.
public boolean verifyState(HttpServletRequest request, String s) {
String stateToken = getCookieByName(request, STATE_TOKEN);
String authToken = getCookieByName(request, AUTHN);
boolean isValidState = true;
if (isValidState) {
try {
log.info(getEdUserId(stateToken, authToken));
return true;
} catch (Exception e) {
ExceptionLogger.logDetailedError("CookieSessionUtils.verifyState", e);
return false;
}
} else {
return false;
}
}
public String getEdUserId(String stateToken, String authToken) throws Exception {
String edUserId;
Map<String, Object> jwtClaims;
jwtClaims = StateUtils.checkJWT(stateToken, this.stateSharedSecret); // Failing here not generating a proper jwt token
log.info("State Claims: " + jwtClaims);
edUserId = sifAuthorizationService.getEdUserIdFromAuthJWT(authToken);
return edUserId;
}
My test:
#ActiveProfiles(resolver = MyActiveProfileResolver.class)
#WebMvcTest(value = CookieSessionUtils.class, includeFilters = {
#ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {ApiOriginFilter.class, ValidationFilter.class})})
class CookieSessionUtilsTest {
#Autowired
private CookieSessionUtils cookieSessionUtils; // Service class
#Mock
private CookieSessionUtils cookieSessionUtilsMocked; // Both the method under test and the one mocked are under the same class, so trying these two annotations together.
#Mock
private HttpServletRequest request;
#BeforeEach
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testVerifyState1() throws Exception {
//...Some mocks for getCookieName
UUID uuid = UUID.randomUUID();
when(cookieSessionUtils.getEdUserId(anyString(), anyString()).thenReturn(eq(String.valueOf(uuid))); // When this line runs it fails on verifyState method
assertTrue(cookieSessionUtils.verifyState(request, ""));
}
UPDATE
Attempt using anyString() instead of eq().
Thank you.
Your test is broken in a few places.
Setting expectations on a real object
You should call Mockito.when on mocks and spies, not on System under test. Mockito normally reports it with a clear error message, but you throw a NPE from getEdUserId, so this is reported instead. The NPE stems from the fact that both eq and anyString return null, which is passed to the real method.
Invalid use of matchers
As #StefanD explained in his answer eq("anyString()") is not matching any string. It matches only one string "anyString()"
Returning a mather instead of real object
thenReturn(eq(String.valueOf(uuid)))
This is illegal position for a matcher.
Mixing Mockito and Spring annotations in a WebMvcTest
This is a common error. Mockito does not inject beans to the spring context.
From the code provided it is unclear what CookieSessionUtils is (Controller? ControllerAdvice?) and what is the correct way to test it.
Update
It seems that you are trying to replace some methods under test. A way to do it is to use a Spy.
See https://towardsdatascience.com/mocking-a-method-in-the-same-test-class-using-mockito-b8f997916109
The test written in this style:
#ExtendWith(MockitoExtension.class)
class CookieSessionUtilsTest {
#Mock
private HttpServletRequest request;
#Mock
private SifAuthorizationService sifAuthorizationService;
#Spy
#InjectMocks
private CookieSessionUtils cookieSessionUtils;
#Test
public void testVerifyState1() throws Exception {
Cookie cookie1 = new Cookie("stateToken", "stateToken");
Cookie cookie2 = new Cookie("Authn", "Authn");
when(request.getCookies()).thenReturn(new Cookie[]{cookie1, cookie2});
UUID uuid = UUID.randomUUID();
doReturn(String.valueOf(uuid)).when(cookieSessionUtils).getEdUserId(anyString(), anyString());
assertTrue(cookieSessionUtils.verifyState(request, ""));
}
}
An alternative way is to call the real method, but to mock all collaborators: StateUtils and sifAuthorizationService. I would probably go with this one, if you want to test public getEdUserId.
Test written when mocking collaborators:
#ExtendWith(MockitoExtension.class)
class CookieSessionUtilsTest {
#Mock
private HttpServletRequest request;
#Mock
private SifAuthorizationService sifAuthorizationService;
#InjectMocks
private CookieSessionUtils cookieSessionUtils;
#Test
public void testVerifyState1() throws Exception {
Cookie cookie1 = new Cookie("stateToken", "stateToken");
Cookie cookie2 = new Cookie("Authn", "Authn");
when(request.getCookies()).thenReturn(new Cookie[]{cookie1, cookie2});
UUID uuid = UUID.randomUUID();
when(sifAuthorizationService.getEdUserIdFromAuthJWT(cookie2.getValue())).thenReturn(String.valueOf(uuid));
assertTrue(cookieSessionUtils.verifyState(request, ""));
}
}
I took the assumption that StateUtils.checkJWT does not need to be mocked
The points above are still valid and need to be resolved in either case.
Remarks
As the system under test is currently a Service, I suggest to drop WebMvcTest and test it with plain mockito instead.
Should SUT be a service? It is more typical to handle auth code in filters.
note usage of doReturn when stubbing a method on a spy.
You use mocks in more places than needed. For example Cookie is trivial to construct, there is no point in using a mock
The error is here:
when(cookieSessionUtils.getEdUserId(eq("anyString()"), eq("anyString()"))).thenReturn(eq(String.valueOf(uuid)));
It should read like
when(cookieSessionUtils.getEdUserId(anyString()), anyString()).thenReturn(uuid);
Please refer to the Mockito documentation of Argument matchers.
Because the argument matchers looking for the string "anyString()" they never match the actual parameters the method call is providing and so there is never returned the uuid you expecting.

Value passed to argumentMatchers.eq() appears null

I'm mocking a method getQuizFromQuizResponse that requires a non-null object. I tried using eq() from the ArgumentMatchers library to pass in an existing object I initialized. When I debug SecurityServiceTest in my IDE, eq(quizResponse) appears exactly how I intend it to. When I reach a breakpoint in the method I'm mocking, getQuizFromQuizResponse(), quizResponse then appears as null.
Why doesn't eq() keep the value I defined when mocking?
For further context:
[OUTDATED] the class of the method I am attempting to mock, QuizMapper, is a Spy. I mention this because I feel this could be why I'm having this issue?
[OUTDATED] The reason I have QuizMapper as a Spy is because we did not create this class to be a bean since it's purpose is to simply map from DTO to another... so it isn't a Service.
[OUTDATED] Since QuizMapper isn't a bean I couldn't autowire it or inject it into SecurityService and attempting to make it a #Mock would have mockito giving me error like so: "Strict stubbing argument mismatch"
I now have QuizMapper as a #Mock because I realized that in verifyUser() I'm calling on a builder which is causing the argument mismatch to have some initialized data which I believe goes against stubbing?
Here's the error:
org.mockito.exceptions.misusing.PotentialStubbingProblem:
Strict stubbing argument mismatch. Please check:
- this invocation of 'checkIdentity' method:
feignClient.checkIdentity(
0L,
null,
Verification(type=Initiate, settings=Settings(mode=null,
reference=Reference1, locale=en_US, venue=online), persons=.
[Person(name=null, addresses=[], ssn=null, phones=null,
emails=null, context=primary, Id=Id)], answers=null)
);
-> at com.*.Service.verifyUser(SecurityService.java:39)
- has following stubbing(s) with different arguments:
1. feignClient.checkIdentity(
0L,
null,
null
);
Here's my code:
#ExtendWith(MockitoExtension.class)
class SecurityServiceTest {
#InjectMocks
private SecurityService securityService;
#Spy
private FeignClient feignClient;
#Spy
private QuizMapper quizMapper;
#Test
void testVerifyUserReturnsQuiz() throws ProviderException {
String id = "Id";
String mode = "test";
Quiz expectedQuiz = new Quiz();
expectedQuiz.setId("quizId");
Verification verification = new VerificationBuilder()
.setType(VerificationBuilder.TYPE)
.setMode(mode)
.setId(id)
.build();
QuizResponse quizResponse = new QuizResponse();
quizResponse.setProducts(new ProductTestUtil().getProductList());
given(
feignClient.checkIdentity(any(Long.class), any(String.class), eq(verification))
).willReturn(singleQuizResponse);
given(
quizMapper.getQuizFromQuizResponse(eq(quizResponse))
).willReturn(expectedQuiz);
Quiz actualQuiz = securityService.verifyUser(id);
assertEquals(actualQuiz, expectedQuiz);
}
}
Here is the class and function I am trying to invoke in my unit test
#Slf4j
#Service
public class SecurityService {
private final FeignClient feignClient;
#Value(“${mode}")
private String settingsMode;
#Value("${accountId}")
private long accountId;
#Value("${workflowName}")
private String workflowName;
public SecurityService(FeignCleint feignClient) {
this.feignClient = feignClient;
}
public Quiz verifyUser(String lexId) throws ProviderException {
QuizMapper quizMapper = new QuizMapper();
Verification user = new VerificationBuilder()
.setType(VerificationBuilder.TYPE)
.setMode(mode)
.setId(Id)
.build();
logger.info("Verification POST request: {}", user);
QuizResponse response = feignClient.checkIdentity(accountId, workflowName, user);
logger.info("QuizResponse POST response: {}", response);
return quizMapper.getQuizFromQuizResponse(response);
}
}

How to do integ test using mockito

I have following two methods which i want to test
public class Orders {
private final LambdaLogger logger;
private final DynamoDBMapper dynamoDBMapper;
public Orders(LambdaLogger logger, AmazonDynamoDB amazonDynamoDB){
this.logger = logger;
this.dynamoDBMapper = new DynamoDBMapper(amazonDynamoDB);
}
public List<Orders> getOrders(){
logger.log("getting all orders");
DynamoDBScanExpression scanExpression = new DynamoDBScanExpression()
.withProjectionExpression("OrderId");
logger.log("Expression created");
PaginatedScanList<Orders> scan = dynamoDBMapper.scan(Orders.class, scanExpression);
return scan.stream()
.collect(Collectors.toList());
}
}
Now, I want to do testing using Mockito for this class. There are couple of things that I am confuse (or unable to get working).
First, DynamoDBMapper is being created using amazonDynamoDBClient. So if in my class i Mock AmazonDynamoDB, how the dynamoDBMapper would get created?
How would I test that my function is actually setting projection right?
How would i test on paginatedScanList?
It is violation of Dependency Injection principle that does not allow you to create a unit test.
Orders should not create any objects it should receive them as dependencies through constructor or setter methods. E.g. you could pass DynamoDBScanExpressionFactory and DynamoDBMapper to Orders' constructor. Then you would be able to mock them with mockito.

Mockito - Desired returned mock object not being used in class-under-test (CUT)

I'm trying to get a handle on Mockito and have a situation where I'd like to use a mock object from within the class-under-test (CUT) but it does not appear to be working. I'm pretty sure I'm approaching the solution incorrectly. Here's some code:
CUT:
public class TestClassFacade {
// injected via Spring
private InterfaceBP bpService;
public void setBpService(InterfaceBP bpService) {
this.bpService = bpService;
}
public TestVO getTestData(String testString) throws Exception {
bpService = BPFactory.getSpecificBP();
BPRequestVO bpRequestVO = new BPRequestVO();
InterfaceBPServiceResponse serviceResponse = bpService.getProduct(bpRequestVO);
if (serviceResponse.getMessage().equalsIgnoreCase("BOB")) {
throw new Exception();
} else {
TestVO testVO = new TestVO();
}
return testVO;
}
}
Spring Configuration:
<bean id="testClass" class="com.foo.TestClassFacade">
<property name="bpService" ref="bpService" />
</bean>
<bean id="bpService" class="class.cloud.BPService" />
Mockito Test Method:
#RunWith(MockitoJUnitRunner.class)
public class BaseTest {
#Mock BPService mockBPService;
#InjectMocks TestClassFacade mockTestClassFacade;
String testString = "ThisIsATestString";
BPRequestVO someBPRequestVO = new BPRequestVO();
InterfaceBPServiceResponse invalidServiceResponse = new BPServiceResponse();
#Test (expected = Exception.class)
public void getBPData_bobStatusCode_shouldThrowException() throws Exception {
invalidServiceResponse.setMessage("BOB");
when(mockBPService.getProduct(someBPRequestVO)).thenReturn(invalidServiceResponse);
mockTestClassFacade.getTestData(testString);
verify(mockBPService.getProduct(someBPRequestVO));
}
}
What I'm trying to do is verify that the "if" conditional portion of the CUT (throwing the Exception) is being invoked in the case of a "BOB" message string being returned from the response of the 3rd party class (BPService). However, what's happening is that the "invalidResponse" object that I am trying to return in the "when" statement above isn't actually being returned when I run my mockTestClassFacade in the line below it. Instead, the
InterfaceBPServiceResponse serviceResponse = bpService.getProduct(bpRequestVO);
line in the real method is being invoked and the "serviceResponse" is being used during my test.
How do I get my mockTestClassFacade to use my "invalidServiceResponse" in this situation?
Thanks a lot...if anything isn't clear please let me know!
I think the problem is in bpService = BPFactory.getSpecificBP();.
You're mocking and injecting InterfaceBP into TestClassFacade, but inside the method getTestData you're creating a new InterfaceBP from BPFactory.
So when testing, you're not actually using the mock, but a different object.
If InterfaceBP is created and injected by Spring, you shouldn't need a factory to get an instance.
Continuing from the other answer, you need to mock the behavior of "BPFactory.getSpecificBP()", but Mockito won't let you mock static methods. You'll have to use PowerMock for this test.

Categories