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.
Related
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);
}
}
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.
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.
Unfortunately the Mockito documentation lacks of exhaustive information, it's difficult to find the proper way how to create the following setup.
I have a class "ResourceManager" which should be tested. This class instantiates another class "JerseyClient" which has a method called "get". I want "ResourceManager" to not invoke the real "JerseyClient" but a mock (or a stub?) from it (It's already unclear to me what is the difference between mocking and stubbing or mocks and spies in Mockito context).
My attempts are to #Mock (or #Spy?) JerseyClient, or at least one method of it:
#RunWith(MockitoJUnitRunner.class)
public class ResourceManagerTest
{
#Mock
private JerseyClient jerseyClient;
#Test
public void testResultHandling() throws JsonGenerationException, JsonMappingException, IOException, ResourceException
{
TestEntity testEntity = new TestEntity();
ResourceManager resourceManager = new ResourceManager();
testEntity.setGet(true);
testEntity.setTestAttribute("1stTest");
when(jerseyClient.get(anyString())).thenReturn("{\"get\":true,\"testAttribute\":\"2ndTest\",\"message\":\"success\"}");
// doReturn("{\"get\":true,\"testAttribute\":\"2ndTest\",\"message\":\"success\"}").when(jerseyClient).get(anyString());
TestEntity result = (TestEntity)resourceManager.execute(testEntity, TestEntity.class);
assertThat(result, is(notNullValue()));
assertThat(result.getMessage(), is("success"));
assertThat(result.getTestAttribute(), is("2ndTest"));
}
}
As you can see, I tried to mock jerseyClient.get() method to return a predefined JSON string:
when(jerseyClient.get(anyString())).thenReturn("{\"get\":true,\"testAttribute\":\"2ndTest\",\"message\":\"success\"}");
or
doReturn("{\"get\":true,\"testAttribute\":\"2ndTest\",\"message\":\"success\"}").when(jerseyClient).get(anyString());
But none of them work. That means that the real JerseyClient.get method is invoked, because it tries to make a HTTP request with the real JerseyClient.
What is the solution and what is this what I want to do here? Is it spying on a real object or mocking an object where I want to mock a method of it, and when can I replace methods, only on mocks or only on spies?
I found the answer on my own. I need to add
#InjectMocks
private ResourceManager resourceManager;
Then I need to run the test with this instance:
#RunWith(MockitoJUnitRunner.class)
public class ResourceManagerTest
{
#Mock
private JerseyClient jerseyClient;
#InjectMocks
private ResourceManager resourceManager;
#Test
public void testResultHandling() throws JsonGenerationException, JsonMappingException, IOException, ResourceException
{
TestEntity testEntity = new TestEntity();
testEntity.setGet(true);
testEntity.setTestAttribute("1stTest");
when(jerseyClient.get(anyString())).thenReturn("{\"get\":true,\"testAttribute\":\"2ndTest\",\"message\":\"success\"}");
TestEntity result = (TestEntity)resourceManager.execute(testEntity, TestEntity.class);
assertThat(result, is(notNullValue()));
assertThat(result.getMessage(), is("success"));
assertThat(result.getTestAttribute(), is("2ndTest"));
}
}
And I can also use the "doReturn...when" pattern.
I want to test a JSF Backing-Bean method "isInProgress" that delegates to a service method "isInProgress". When the service method throws an exception, the bean should put an event on a specific event logger and return false.
When I debug the following test, I get into the catch-block. The mocked service does not throw the exception, but returns a "default answer" which is false for th boolean. What am I doing wrong?
I also wonder if the try-catch around the "when" call can be avoided somehow, as the actual exception is swallowed by the bean under test. In fact I think "declaratively" passing the name of the method to the "when" should suffice. Is there a way to get that cleaner ?
#Test
public void testIsInProgressExeption() {
//prepare object and inputs
MyBean bean = new MyBean();
MyService service = mock(MyAdapterService.class);
bean.setService(service);
try {
when(bean.getService().isInProgress()).thenThrow(new Exception());
} catch (Exception e) {
//prepare expected object and result
MyBean expectedBean = new MyBean();
expectedBean.setService(service);
boolean expected = false;
//execute method under test
boolean actual = bean.isInProgress();
//check return values and exceptions
assertEquals(expected, actual);
//check that bean did not change unexpectedly
assertTrue(bean.equals(expectedBean));
//check sideeffects on event log
assertTrue(logEvents.containsMessage("MDI09"));
}
}
For reference here is the updated Test:
#Test
public void testIsInProgressExeption() throws Exception {
//prepare object and inputs
MyBean bean = new MyBean();
MyService service = mock(MyAdapterService.class);
bean.setService(service);
when(bean.getService().isInProgress()).thenThrow(new Exception());
//prepare expected object and result
MyBean expectedBean = new MyBean();
expectedBean.setService(service);
boolean expected = false;
//execute method under test
boolean actual = bean.isInProgress();
//check return values and exceptions
assertEquals(expected, actual);
//check that bean did not change unexpectedly
assertTrue(bean.equals(expectedBean));
//check sideeffects on event log
assertTrue(logEvents.containsMessage("MDI09"));
}
Move the when clause out of the try block and change it to:
when(service.isInProgress()).thenThrow(new Exception());
Now it should throw an exception when called.
For the records, I was doing state-base testing. Interestingly, Fowler posted in http://martinfowler.com/articles/mocksArentStubs.html a very nice article that goes quite the same route but then differentiates it from mocking and interaction-based testing.
You are doing it wrong. First you should lay out your test with the BDD or AAA keywords, with BDD :
#Test public void testIsInProgressExeption() {
// given
// when
// then
}
In the given part you will write your fixture, i.e. the setup of your test scenario. In the when part you will call the production code i.e. the tested subject. Lastly in the when part you will write your verifications and or assertions.
Stubs go in the fixture, so this line is misplaced, it doesn't belong here, it' just a definition of the behavior.
when(bean.getService().isInProgress()).thenThrow(new Exception());
However you should directly the service reference instead of the bean.getService(), this is akward.
I don't really understand why you are creating a new instance of the bean in the catch clause, this is weird. But here's how I wold write the test. Note by the way I explain in the unit test name what behavior the test is actully testing, writing this in camel case is way to painful to read, so I use the underscored convention, it's ok in tests.
#Test public void when_service_throw_Exception_InProgress_then_returns_false() throws Exception {
// given
MyBean bean = new MyBean();
MyService service = mock(MyAdapterService.class);
bean.setService(service);
when(service.isInProgress()).thenThrow(new Exception());
// when
boolean result = bean.isInProgress();
// then
assertFalse(result);
}
Also I would split the assertion on the event, this a different behavior :
#Test public void when_service_throw_Exception_InProgress_then_log_event_MDI09() throws Exception {
// given
MyBean bean = new MyBean();
MyService service = mock(MyAdapterService.class);
bean.setService(service);
// somehow set up the logEvents collaborator
when(service.isInProgress()).thenThrow(new Exception());
// when
bean.isInProgress();
// then
assertTrue(logEvents.containsMessage("MDI09"));
}
You can even go further to simplify the fixture, if you use JUnit, you write this code :
#RunWith(MockitoJUnitRunner.class)
public class MyBeanTest {
#Mock MyService service;
#Mock LogEvents logEvents;
#InjectMocks MyBean bean;
#Test public void when_service_throw_Exception_InProgress_then_log_event_MDI09() throws Exception {
// given
when(service.isInProgress()).thenThrow(Exception.class);
// when
bean.isInProgress();
// then
verify(logEvents).logEvent("MDI09");
}
}
In the example above I also extrapolated on the log event stuff, but it is just to give the idea of what's possible.