Java Mockito.when failing with StringBuilder as parameter - java

Below is the JUnit Test, oneLoginAuthUtil is Mocked. But Mockito.when is returning null. oneLoginAuthUtil.getMetaData is always null. Below is the code -
public void func() throws Exception {
StringBuilder b = new StringBuilder("test");
RequestContext context = new RequestContext();
Mockito.when(oneLoginAuthUtil.getMetaData(context, b)).thenReturn("abcdef");
ResponseEntity<Object> response = loginControllerImpl.handleGetMetaDataEndPointImpl(context);
}
public String getMetaData(RequestContext context, StringBuilder b) throws Exception {
Auth auth = getOneLoginAuthObject(context);
final Saml2Settings settings = auth.getSettings();
String metadata = settings.getSPMetadata();
List<String> errors = Saml2Settings.validateMetadata(metadata);
if (!errors.isEmpty()) {
b.append(errors.toString());
throw new SSOException("metadata_validation_error");
}
return metadata;
}
public ResponseEntity<Object> handleGetMetaDataEndPointImpl(RequestContext context) {
try {
StringBuilder b = new StringBuilder();
String metadata = oneLoginAuthUtil.getMetaData(context, b);
log.info(metadata);
return new ResponseEntity<>(metadata, new HttpHeaders(), HttpStatus.CREATED);
} catch (Exception e) {
return new ResponseEntity<>("<error>Exception: " + e.getMessage() + "</error>", new HttpHeaders(),
HttpStatus.CREATED);
}
}
But oneLoginAuthUtil.getMetaData method without StringBuilder as parameter works fine.

It returns null because when you mock a method using specific instances as parameters, Mockito will return the mapped return value if and only if the parameters provided while testing are equal to the parameters that you provided while mocking which is obviously not the case. Mockito did not have a match so it returns the default value for a method that returns an object called on a mock which is null.
In other words Mockito.when(oneLoginAuthUtil.getMetaData(context, b)) is equivalent to Mockito.when(oneLoginAuthUtil.getMetaData(Mockito.eq(context), Mockito.eq(b))), here as the class StringBuilder doesn't override the method equals(Object obj), the instance provided while mocking is not equal to the instance provided while testing as they are not the same instance (the only possibility to be equal when relying on the default implementation of equals(Object obj)) such that you get this behavior.
So you should use Mockito.any(StringBuilder.class) to match with any instances of type StringBuilder such that the instances don't need to be equal anymore, your code would then be:
Mockito.when(
oneLoginAuthUtil.getMetaData(Mockito.eq(context), Mockito.any(StringBuilder.class))
).thenReturn("abcdef");
Assuming that your main class is LoginControllerImpl and that it has a member field oneLoginAuthUtil of type OneLoginAuthUtil, you could use the annotation #InjectMocks to inject directly your mock of type OneLoginAuthUtil, the complete code would then be:
#RunWith(MockitoJUnitRunner.class)
public class LoginControllerImplTest {
#Mock
private OneLoginAuthUtil oneLoginAuthUtil;
#InjectMocks
private LoginControllerImpl loginControllerImpl;
#Test
public void func() throws Exception {
RequestContext context = new RequestContext();
Mockito.when(
oneLoginAuthUtil.getMetaData(
Mockito.eq(context), Mockito.any(StringBuilder.class)
)
).thenReturn("abcdef");
ResponseEntity<Object> response
= loginControllerImpl.handleGetMetaDataEndPointImpl(context);
...
}
}

StringBuilder does not implement equals() or hashCode(), so it can't be used for argument matching like that.
The problem really is that you shouldn't be passing a mutable object (such as StringBuilder) into your test subject. Why does returning "metadata" require you to append to a StringBuilder outside your scope?

Related

java.lang.AssertionError when executing unit test when using my own model as parameter

I got a AssertionError when I execute the unit test. response.getBody() is null.
Here is unit test method;
public void when_CustomerNumNotNull_Expect_TspResult() {
/*
some code
*/
TspResultDto tspResultDto = new TspResultDto();
tspResultDto.setTspResult("blabla");
Mockito.doReturn(tspResultDto).when(creditService)
.getTspResult(tspInputDto);
ResponseEntity<TspResponse> response = creditController
.getTspResult(TspRequest);
Assert.assertNotNull(response.getBody()); // error occured this line because body null.
Assert.assertEquals(HttpStatus.OK, response.getStatusCode());
//Assert.assertEquals(true, response.getBody().isHrResult());
}
}
TspInputDto and TspRequest are my model Class.
But I don't get an error when I run it with a single parameter like below without needing the model class.
Mockito.doReturn(newCreditApplicationDto).when(creditService)
.getNewCreditApplicationNo(customerNum);
ResponseEntity<NewCreditApplicationResponse> response = creditController
.getNewCreditApplicationNo(customerNum);
Assert.assertNotNull(response.getBody());
Assert.assertEquals(HttpStatus.OK, response.getStatusCode());
Here is controller;
public ResponseEntity<TspResponse> getTspResult(
TspRequest tspRequest) {
TspInputDto tspInputDto = creditDemandRestMapper
.toTspInputDto(tspRequest);
TspResultDto tspResultDto = creditService
.getTspResult(tspInputDto);
TspResponse tspResponse = creditDemandRestMapper
.toTspResponse(tspResultDto);
return new ResponseEntity<>(tspResponse, HttpStatus.OK);
}
Here is service;
public TspResultDto getTspResult(
TspInputDto tspInputDto) {
TspResultDto tspResultDto = new TspResultDto();
/*
Some code here...
*/
return tspResultDto;
}
Where am I doing wrong?
In this line:
Mockito.doReturn(tspResultDto).when(creditService)
.getTspResult(tspInputDto);
you set the mock to return your tspResultDto object if the argument equals() to tspInputDto, meaning it should be an equal object (in terms of equals() comparison). Probably your TspInputDto class doesn't have equals method defined properly.
Anyways, I'd propose to rewrite this line of code using argThat matcher:
Mockito.doReturn(tspResultDto).when(creditService)
.getTspResult(ArgumentMatchers.argThat(tspInputDto ->
// condition for provided tspInputDto
));
Have you mocked the creditController? If you haven't then it will not give you response mock object.

Mocking class is returning null instead of data

In my Junit test, I'm doing the following in my Junit test :
#Before
public void setUp() throws Exception {
reportQueryParams = ReportQueryParams.builder()
.id("07")
.build();
}
#Test
public void tabSerializerTest() {
MetricsSerializer mockMonth = mock(MetricsSerializer.class);
when(mockMonth.getCurrentMonth()).thenReturn("July");
String tabSeparated = mockMonth.serializeMetrics(reportQueryParams);
String expected = new StringBuilder().append("074")
.append("\t")
.append("July")
.toString();
assertEquals(expected, tabSeparated);
}
The function which I am testing:
public String serializeMetrics(final ReportQueryParams reportQueryParams) {
stringJoiner = new StringJoiner("\t");
addValueFromString(reportQueryParams.getId());
addValueFromString(getCurrentMonth());
return stringJoiner.toString();
}
public String getCurrentMonth() {
DateFormat monthFormat = new SimpleDateFormat("MMMMM");
return monthFormat.format(new Date());
}
private void addValueFromString(final String value) {
stringJoiner.add(value);
}
My ReportQueryParams class:
public class ReportQueryParams {
private String id;
}
I am getting "null" in the actual data returned and hence the test is failing. How can I fix this?
Don't mock the object you test.What you have written is "create a mock object that returns July for current month". But this mock object doesn't have real behaviour and the other methods return null.
When you test a class you mock the objects required by the class (in order to insulate behaviour) and not the actual class. Here you can create a new MetricsSerializer (by using new :) and call it's method serializeMethod and compare against the current date (instead of July).
The way you have written the class might not be the best testable way though ;)
Your problem is that you are mocking the class, then testing the mock object, rather than testing a "real" object. I can think of two possible solutions.
Use a Mockito Spy instead of a mock. This is like a mock, but it's a real object, and the methods all have their normal behaviour, instead of "no behaviour" by default. You can stub the getCurrentMonth method of your spy, to make it return what you want.
Since the real cause of your problem is the call to new Date(), you could use a time helper, instead of calling new Date() directly in your getCurrentMonth() method. I have described this technique in detail in my answer to this question

How to effective verify a method if it called during unit test with Powermock

When I used Powermock to mock a private method, and then verify it in another method's unit test, to check if it was called and with correct times. But I found I can verify whether the method was called and with the correct param, the called times of the method can be any number, however the unit test always passed. If someone can help me solve the problem. thx!
This is my source code:
public enum ConversionCommon{
INSTANCE;
...
public void getConvertTypeData(JSONObject result, JSONObject conversionData, String intent){
String uncertainty = "";
String unit1 = "";
String unit1_char = "";
if(result.has(ConversionDefine.CONVERSION_UNCERTAINTY)){
uncertainty = result.getString(ConversionDefine.CONVERSION_UNCERTAINTY);
}
if(result.has(ConversionDefine.CONVERSION_UNIT1)){
unit1 = result.getString(ConversionDefine.CONVERSION_UNIT1);
}
if(result.has(ConversionDefine.CONVERSION_UNIT1_CHAR)){
unit1_char = result.getString(ConversionDefine.CONVERSION_UNIT1_CHAR);
}
setUnitCount(conversionData, uncertainty, intent);
setDestUnits(conversionData, unit1, unit1_char);
}
}
The method getConvertTypeData() calls two methods once. The follow is my test case code:
#RunWith(PowerMockRunner.class)
#PrepareForTest({ ConversionCommon.class, LengthConversion.class })
#PowerMockIgnore("javax.management.*")
public class ConversionCommonTest{
#Before
public void setUp() throws Exception{
PowerMockito.mockStatic(LengthConversion.class);//
PowerMockito.spy(LengthConversion.class); //
}
#Test
public void should_getConvertTypeData_invoke_setUnitCount() throws Exception{
JSONObject result = new JSONObject();
JSONObject conversionData = new JSONObject();
result.put(ConversionDefine.CONVERSION_UNCERTAINTY, "3");
result.put(ConversionDefine.CONVERSION_UNIT1, "meter");
result.put(ConversionDefine.CONVERSION_UNIT1_CHAR, "centmeter");
suppress(method(ConversionCommon.class, "setUnitCount", JSONObject.class, String.class, String.class));
ConversionCommon.INSTANCE.getConvertTypeData(result, conversionData, ConversionDefine.INTENT_LENGTH_CONVERSION);
PowerMockito.verifyPrivate(ConversionCommon.INSTANCE, Mockito.times(1)).invoke("setUnitCount", Mockito.anyObject(), Mockito.anyString(), Mockito.anyString());
}
The test case can run successful but if I change the last line to ...
PowerMockito.verifyPrivate(ConversionCommon.INSTANCE, Mockito.times(100)).invoke("setUnitCount", Mockito.anyObject(), Mockito.anyString(), Mockito.anyString());
... and re-run the test case. The result is also passed. But in fact I only call the method setUnitCount only once in Refo.
The edition of test framework: mockito:1.10.19;powermock:1.6.5
How can I correct verify the private method's call times with the API PowerMockito.verifyPrivate();
You are doing right, except one thing. You're spying on enum constant. And each enums constant is separate inner class. So you have to add two things:
Enum constant to #PrepareForTest with using fullyQualifiedNames, like `#PrepareForTest(values = { ConversionCommon.class, LengthConversion.class }, fullyQualifiedNames = "com.somepackage.ConversionCommon$INSTANCE")
Create a spy for instance: PowerMockito.spy(ConversionCommon.INSTANCE)
Replace constant with spy: Whitebox.setInternalState(ConversionCommon.class, spy)
Now, you can stub the spy method as usual doNothing().when(spy, "setUnitCount");
Code snippet for some abstract singleton implement via enum:
enum
public enum Singleton {
INSTANCE;
public void doSomething(){
callPrivateMethod();
}
private void callPrivateMethod() {
}
}
test
RunWith(PowerMockRunner.class)
#PrepareForTest(value = Singleton.class, fullyQualifiedNames = "com.stackoverflow.q46212600.Singleton$INSTANCE")
public class ClassUseSingletonTest {
#Test
public void should_verify_enum_method() throws Exception {
final Singleton spy = PowerMockito.spy(Singleton.INSTANCE);
Whitebox.setInternalState(Singleton.class, spy);
doNothing().when(spy, "callPrivateMethod");
new ClassUseSingletone().doSomething("do");
PowerMockito.verifyPrivate(Singleton.INSTANCE, times(1)).invoke("callPrivateMethod");
}
}
Full example, you may find here

Complex JUnit test Case

I am writing a test case using JUnit for a method that takes an ENUM in the switch statement.
This is the method to be tested.
public <T extends BaseServiceResponse> T postProcess(T response,
ClientResponse clientResponse) throws EISClientException {
List<Message> messages = response.getMessages();
if(messages==null || messages.size()==0) {
return response;
}
Map<String, Message> messagesMap = populateMessages(response.getMessages());
ConditionOperator condition = getCondition();
switch(condition) {
case OR:
checkORCondition( messagesMap );
break;
case AND:
checkANDCondition( messagesMap );
break;
}
return response;
}
What I've done so far is:
#Test
public void testPostProcess() throws Exception {
clientResponse = mock(ClientResponse.class);
RetrieveBillingServiceResponse response = new RetrieveBillingServiceResponse();
BillingOverview billingOverView = new BillingOverview();
Message message = new Message();
message.setMessageCode("200");
message.setMessageType(MessageTypeEnum.MESSAGE_TYPE_INFO);
message.setMessageText("Service completed successfully");
response.setEntity(billingOverView);
response.setMessages(Arrays.asList(message));
MessageToExceptionPostProcessFilter postProcessFilter = new MessageToExceptionPostProcessFilter();
RetrieveBillingServiceResponse serviceResponse = postProcessFilter.postProcess(response, clientResponse);
assertEquals("200", serviceResponse.getMessages().get(0).getMessageCode());
I am getting a NullPointerException for conditonOperator which is of type ENUM and this holds only two members OR and AND which are the cases in the switch statement.
Can someone help me out how should I proceed with this test.
Thanks
Enum variables can be null. The getCondition() method is returning null. Why it's returning null, we can't really guess without seeing code you haven't shown us.
If getCondition() is private method you cannot mock it. If it is public method, you can mock it or if you have a setCondition, you can directly set the ENUM.
Assuming if you are using Mockito or PowerMock or EasyMock you can use something like
when(postProcessFilter.getCondition()).thenReturn(Enum.OR);

mockito verify interactions with ArgumentCaptor

To check the number of interactions with a mock where the parameter in the method call is of a certain type, one can do
mock.someMethod(new FirstClass());
mock.someMethod(new OtherClass());
verify(mock, times(1)).someMethod(isA(FirstClass.class));
This will pass thanks to the call to isA since someMethod was called twice but only once with argument FirstClass
However, this pattern seems to not be possible when using an ArgumentCaptor, even if the Captor was created for the particular argument FirstClass
this doesn't work
mock.someMethod(new FirstClass());
mock.someMethod(new OtherClass());
ArgumentCaptor<FirstClass> captor = ArgumentCaptor.forClass(FirstClass.class);
verify(mock, times(1)).someMethod(captor.capture());
it says the mock was called more than once.
Is there any way to accomplish this verification while capturing the argument for further checking?
I recommend using Mockito's Hamcrest integration to write a good, clean matcher for it. That allows you to combine the verification with detailed checking of the passed argument:
import static org.mockito.hamcrest.MockitoHamcrest.argThat;
verify(mock, times(1)).someMethod(argThat(personNamed("Bob")));
Matcher<Person> personNamed(final String name) {
return new TypeSafeMatcher<Person>() {
public boolean matchesSafely(Person item) {
return name.equals(item.getName());
}
public void describeTo(Description description) {
description.appendText("a Person named " + name);
}
};
}
Matchers generally lead to more readable tests and more useful test failure messages. They also tend to be very reusable, and you'll find yourself building up a library of them tailored for testing your project. Finally, you can also use them for normal test assertions using JUnit's Assert.assertThat(), so you get double use out of them.
Quoting the docs:
Note that an ArgumentCaptordon't do any type checks, it is only
there to avoid casting in your code. This might however change (type
checks could be added) in a future major release.
I wouldn't use an ArgumentCaptor for this. This class captures (literally) everything, despite what class was provided as it's .forClass argument.
To achieve what you want I suggest intercept the argument using Mockito's Answer interface:
private FirstClass lastArgument;
#Test
public void captureFirstClass() throws Exception {
doAnswer(captureLastArgument()).when(mock).someMethod(anInstanceOfFirstClass());
mock.someMethod(new FirstClass());
mock.someMethod(new OtherClass());
verify(mock, times(1)).someMethod(anInstanceOfFirstClass());
//write your desired matchers against lastArgument object
}
private Answer<FirstClass> captureLastArgument() {
return new Answer<FirstClass>() {
#Override
public FirstClass answer(InvocationOnMock invocation) throws Throwable {
TestClass.this.lastArgument = (FirstClass) invocation.getArguments()[0];
return null;
}
};
}
private static Object anInstanceOfFirstClass(){
return Mockito.argThat(isA(FirstClass.class));
}
You can use the the captor for the sake of capturing, then verify the number of invocations with each argument type separately.
// given
ArgumentCaptor<AA> captor = ArgumentCaptor.forClass(AA.class);
CC cc = new CC();
// when
cut.someMethod(new AA());
cut.someMethod(new BB());
cut.someMethod(new BB());
cut.someMethod(cc);
// then
Mockito.verify(collaborator, atLeastOnce()).someMethod(captor.capture());
Mockito.verify(collaborator, times(1)).someMethod(isA(AA.class));
Mockito.verify(collaborator, times(2)).someMethod(isA(BB.class));
Mockito.verify(collaborator, times(1)).someMethod(isA(CC.class));
assertEquals(cc, captor.getValue());
Apparently the generic type of the captor reference doesn't affect anything at runtime.
I also encountered this problem today. I thought I could simply do something like
verify(mock).someMethod(and(isA(FirstClass.class), captor.capture()));
but I couldn't get it to work. I ended up with this solution:
#Test
public void Test() throws Exception {
final ArgumentCaptor<FirstClass> captor = ArgumentCaptor.forClass(FirstClass.class);
mock.someMethod(new FirstClass());
mock.someMethod(new OtherClass());
verify(eventBus, atLeastOnce()).post(captor.capture());
final List<FirstClass> capturedValues = typeCheckedValues(captor.getAllValues(), FirstClass.class);
assertThat(capturedValues.size(), is(1));
final FirstClass capturedValue = capturedValues.get(0);
// Do assertions on capturedValue
}
private static <T> List<T> typeCheckedValues(List<T> values, Class<T> clazz) {
final List<T> typeCheckedValues = new ArrayList<>();
for (final T value : values) {
if (clazz.isInstance(value)) {
typeCheckedValues.add(value);
}
}
return typeCheckedValues;
}
Note: if only one class needs to be captured in this way typeCheckedValues can be simplified into:
private static List<FirstClass> typeCheckedValues(List<FirstClass> values) {
final List<FirstClass> typeCheckedValues = new ArrayList<>();
for (final Object value : values) {
if (value instanceof FirstClass) {
typeCheckedValues.add((FirstClass) value);
}
}
return typeCheckedValues;
}

Categories