I'm new to Java / Mockito and trying to test a Dao method, specifically an exception condition that catches ParseException and throws SQLException.
Here's the Dao code:
public Template saveTemplate(Template template) throws SQLException {
logger.debug("Saving template details into the db ", template.getTemplateName());
SimpleDateFormat dt = new SimpleDateFormat("yyyyy-mm-dd hh:mm:ss");
Long date = 0L;
try {
date = dt.parse(template.getStartTime()).getTime();
} catch (ParseException e) {
throw new SQLException("Error while processing date " + template.getTemplateName());
}
Long finalDate = date;
My strategy was to mock the SimpleDateFormat.parse() call so that it throws the ParseException, but that's not working. Not even sure that's a good strategy...
First I tried:
#InjectMocks private SimpleDateFormat simpleDateformat;
but that doesn't work because the SimpleDateFormat constructor requires a parameter, and gets the error:
org.mockito.exceptions.base.MockitoException:
Cannot instantiate #InjectMocks field named 'simpleDateFormat' of type 'class java.text.SimpleDateFormat'.
You haven't provided the instance at field declaration so I tried to construct the instance.
However the constructor or the initialization block threw an exception : null
So then I tried this:
#Mock
private SimpleDateFormat simpleDateFormat;
#Test(expected = SQLException.class)
public void test_save_template_date_parse_error() throws ParseException, SQLException {
initMocks(this);
Mockito.mockingDetails(simpleDateFormat);
Mockito.when(simpleDateFormat.parse(anyString(),new ParsePosition(anyInt()))).thenThrow(new ParseException(anyString(),anyInt()));
Template template = new Template();
template.setStartTime("2017-01-02 12:12:12");
template.setTemplateId(1);
given(jdbcTemplate.getJdbcOperations()).willReturn(jdbcOperations);
templateDAOImpl.saveTemplate(template);
}
The resulting error isn't helpful to my unpracticed eye:
org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
Misplaced or misused argument matcher detected here:
-> at com.macys.etap.ee.dao.TemplateDAOImplTest.test_save_template_date_parse_error(TemplateDAOImplTest.java:77)
-> at com.macys.etap.ee.dao.TemplateDAOImplTest.test_save_template_date_parse_error(TemplateDAOImplTest.java:77)
You cannot use argument matchers outside of verification or stubbing.
Examples of correct usage of argument matchers:
when(mock.get(anyInt())).thenReturn(null);
doThrow(new RuntimeException()).when(mock).someVoidMethod(anyObject());
verify(mock).someMethod(contains("foo"))
This message may appear after an NullPointerException if the last matcher is returning an object
like any() but the stubbed method signature expect a primitive argument, in this case,
use primitive alternatives.
when(mock.get(any())); // bad use, will raise NPE
when(mock.get(anyInt())); // correct usage use
Also, this error might show up because you use argument matchers with methods that cannot be mocked.
Following methods *cannot* be stubbed/verified: final/private/equals()/hashCode().
Mocking methods declared on non-public parent classes is not supported.
So how do I mock this thing and get the error thrown?
Edit: New approach as suggested, mocking Template.getStartTime():
#Test(expected = SQLException.class)
public void test_save_template_date_parse_error() throws ParseException, SQLException {
initMocks(this);
Template templateMock = Mockito.mock(Template.class);
Mockito.when(templateMock.getStartTime()).thenReturn("invalid");
Mockito.mockingDetails(templateMock.getStartTime());
Template template = new Template();
template.setStartTime("2017-01-02 12:12:12");
template.setTemplateId(1);
given(jdbcTemplate.getJdbcOperations()).willReturn(jdbcOperations);
// Fixed per #Daniel Pryden : works now
templateDAOImpl.saveTemplate(templateMock);
}
And now works with the fix.
In my opinion, you don't even need Mockito here, you can simply do the following :
Template template = new Template();
template.setStartTime("THIS IS AN INVALID DATE");
template.setTemplateId(1);
templateDAOImpl.saveTemplate(template);
And then the SQLException will be thrown.
SimpleDateFormat cannot be mocked at all, because you are creating a new instance inside a method, so the mock will be never applied.
Possibilities:
change class structure (e.g put SimpleDateFormat as a constuctor parameter, then InjectMocks annotation will work)
pass invalid data for parse method to break it
use PowerMockito whenNew method, however it should be finality
Mocks aren't magic. They're just objects, and so they obey the same rules as all other objects in the Java language. Most importantly: mock objects don't magically take the place of real objects. You need to pass a reference to the mock instead of a reference to the real object.
In your updated question, you show this code:
Template templateMock = Mockito.mock(Template.class);
Mockito.when(templateMock...)...
Template template = new Template();
...
templateDAOImpl.saveTemplate(template);
That is: you're setting up an object called templateMock which has type Template and is configured with the behavior you want, but then the object you actually pass into templateDAOImpl.saveTemplate is not that object!
This means that all your code setting up the templateMock is effectively dead code: since the value you pass into saveTemplate was constructed using new Template() then it is not a mock.
More generally: Mockito never does anything that you couldn't do by yourself. For example, in this case, you could simply make your own subclass of Template:
private static class FakeTemplate extends Template {
#Override
public String getStartTime() {
return "invalid date";
}
// override other methods as necessary/desired
}
// in your test:
Template fakeTemplate = new FakeTemplate();
templateDAOImpl.saveTemplate(fakeTemplate);
That is all that Mockito is doing when you mock out Template. Mockito just does it in a "fancier" way so that you have less boilerplate code to write. But if you don't understand what Mockito is doing then you shouldn't feel compelled to use it. Always write code you understand, and then you'll always be able to debug it if something goes wrong.
(Aside: There are dirty hacks that can make the new operator instantiate something different than what it was compiled to do -- and that's how PowerMockito does its "magic" -- but that's never necessary and I would never recommend it.)
Just for the record, I found another way to implement this requirement where I used a spy and mocked pretty much everything out; it may help someone else.
#InjectMocks
final Template partiallyMockedTemplate = spy(new Template());
#Test(expected = SQLException.class)
public void test_save_template_date_parse_error() throws SQLException {
initMocks(this);
Template template = new Template();
doReturn("2018-05-44").when(partiallyMockedTemplate).getStartTime();
partiallyMockedTemplate.setStartTime("2017-01-02 12:12:12");
partiallyMockedTemplate.setTemplateId(1);
given(jdbcTemplate.getJdbcOperations()).willReturn(jdbcOperations);
templateDAOImpl.saveTemplate(partiallyMockedTemplate);
}
Related
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
I have actual method below:
public ResponseEntity<Message> catEnter(#PathVariable("catId") BigInteger catId, #RequestBody Catrequest catReq, HttpServletRequest request) throws CatDataException, InvalidCatExcecatption {
Message message = new Message();
try {
message = catManager.submitData(catReq.getMessage(), catId, request);
} catch (IOException e) {
throw new CatDataAppException(e.getMessage());
}
return (ResponseEntity<Message>) restResponse(message, request.getMethod());
// Getting null pointer exception in above line
}
I am using mockito for my test code as below:
#Test
public void submitData() throws Exception {
Message mes = new Message();
mes.setCode("00");
mes.setMessage("hi");
ResponseEntity<Message> responseentity = ((ResponseEntity<Message>) catController.catEnter(BigInteger.valueOf(3431), catRequest, mockRequest));
}
I'm getting null pointer exception, Message going as a null, even I set the value explicitly?
Here:
You pass mockRequest when making that call to your production code:
ResponseEntity<Message> responseentity = ... catController.catEnter(... mockRequest));
And your production call does:
return (ResponseEntity<Message>) restResponse(message, request.getMethod());
So the only conclussion: mockRequest is null!
So, first make sure that the passed variable is not null; like:
Request mockedRequest = mock(Request.class);
Or, use the #Mock annotation in case that mockedRequest is a field in your test class.
On top of that; you probably want to do some mock-specification, like:
when(mockedRequest.getMethod()).thenReturn( whatever )
But beyond that, you are lacking a lot of the fundamental basics of Java:
naming conventions: variable names go camelCase, like entityResponse. And typically, tests are named like testCatEnter to express the method that is tested.
You have casts ... where they are not required.
You have quite some code there ... that is unused, like the mes declaration in your test method.
Long story short: I have the feeling that you are overburdening yourself dramatically. First learn the basics; then go for the advanced Mockito stuff.
I have following code preparing mocks to test my service using Cassandra (I need to mock com.datastax.driver.core.ColumnDefinitions.Definition) :
#RunWith(PowerMockRunner.class)
public class TestMyClass{
private MyClass target;
#Before
public void setUp() throws Exception {
ColumnDefinitions mockColumnDefinitions=Mockito.mock(ColumnDefinitions.class);
Mockito.when(mockRow.getColumnDefinitions()).thenReturn(mockColumnDefinitions);
target= new MyClass();
Definition mockDef = Mockito.mock(Definition.class);
List<Definition> defList = new ArrayList<Definition>();
defList.add(mockDef);
Iterator mockIterator = Mockito.mock(Iterator.class);
Mockito.when(mockColumnDefinitions.iterator()).thenReturn(mockIterator);
Mockito.when(mockIterator.hasNext()).thenReturn(true, false);
Mockito.when(mockIterator.next()).thenReturn(mockDef);
Mockito.when(mockDef.getName()).thenReturn(NAME);
}
#Test
public void testMyMethod() throws Exception {
target.MyMethod();
}
}
Test execution goes fine this place, and I have this type of code in different places, so it should work.
Inside the service I am testing I have following code:
ColumnDefinitions colDef = row.getColumnDefinitions();
Iterator<Definition> defIterator = colDef.iterator();
while (defIterator.hasNext()) {
Definition def = defIterator.next();
String columnName = def.getName();
}
When I debug this code, I see, that both colDef and defIterator are mocked successfully. I see something like that in debug variables area:
Mock for Iterator, hashCode: 430126690
But after defIterator.next() invocation I see that though def is an object and not null, it doesn't show hashcode like for Iterator, instead I see this:
com.sun.jdi.InvocationException occurred invoking method.
And after invoking this string:
String columnName = def.getName();
I immediately get NullPointerException like if def is null.
What am I doing wrong?
Thanks.
EDIT 1 ________________________________________________________________________
I also tried to use PowerMockito with the same methods instead, the result is the same.
EDIT 2 ________________________________________________________________________
I added the whole test method code.
It is been a while since this question was created. I have faced this same problem few days ago and I have solved it in the following manner (I hope my proposed solution helps someone in the future):
First of all, I want to clarify that ColumnDefinition.Definition class is a public static nested class that has four private final fields and only has one constructor: Definition (String keyspace, String table, String name and DataType type) (for more details please refer to the ColumnDefinitions.Definition javadoc and ColumnDefinitions source code). Therefore this nested class could not be mocked by Mockito nor Powermock because of its final fields.
SOLUTION:
I had to create a real object, not a mocked one of the class ColumnDefinition.Definition using reflection, so you can initialise the mockDef object as follows:
Constructor<Definition> constructor = (Constructor<Definition>) Definition.class.getDeclaredConstructors()[0]; // as Definition only has one constructor, 0 will be passed as index
constructor.setAccessible(true);
Definition mockDef = constructor.newInstance("keyspace", "table", "name", null);
replacing this line of code in your snippet:
Definition mockDef = Mockito.mock(Definition.class);
Then the NullPointerException will never be thrown again when executing this line of code:
String columnName = def.getName();
I have a method what I want to test. This method can throw an exception.
mapper.mapToDTO(fragment.getDTO(), new ElementHandler());
I want to test, that what happens after the Exception. So I made a Mocked test:
when(mapper.mapToDTO(dto, Mockito.any(ElementHandler.class))).thenThrow(
new MappingFailureException());
Unfortunatelly this Mocking is not good. I also know that the Mockito.any part is not good. my goal would be to invoke the MappingFailureException
How can I map an Object of a type of a class, that my Exception will be thrown if any type of ElementHandler class is given as a parameter?
Try this
when(mapper.mapToDTO(Mockito.eq(dto), Mockito.any(ElementHandler.class))).thenThrow(
new MappingFailureException());
Considering mapper is mocked...
Mapper mapper = mock(Mapper.class);
Yo can do something like this to try (it should be the same as your test)
doThrow(new MappingFailureException()).when(mapper).mapToDTO(dto, Mockito.any(ElementHandler.class));
If not you can build your custom answer with mockito (in the example it returns an String but change it to the return value of mapToDTO)
when(mapper.mapToDTO(dto, Mockito.any(ElementHandler.class))).thenAnswer(new Answer<String>() {
#Override
public String answer(InvocationOnMock invocation) throws Throwable {
throw new MappingFailureException();
}
});
Hope it helps!
I'm trying to implement Mockito to test a particular method but the .thenReturn(...) seems to always be returning a null object instead of what I intended:
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 {
BPRequestVO bpRequestVO = new BPRequestVO();
bpRequestVO.setGroupNumber(testString) ;
bpRequestVO.setProductType("ALL") ;
bpRequestVO.setProfileType("Required - TEST") ;
IBPServiceResponse serviceResponse = bpService.getProduct(bpRequestVO); //PROBLEM
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;
private String testString = null;
private BPRequestVO someBPRequestVO = new BPRequestVO();
private IBPServiceResponse invalidServiceResponse = new BPServiceResponse();
#Test (expected = Exception.class)
public void getBPData_bobStatusCode_shouldThrowException() throws Exception {
invalidServiceResponse.setMessage("BOB");
someBPRequestVO.setGroupNumber(null);
someBPRequestVO.setProductType("ALL");
someBPRequestVO.setProfileType("Required - TEST");
System.out.println("1: " + someBPRequestVO.getGroupNumber());
System.out.println("2: " + someBPRequestVO.getProductType());
System.out.println("3: " + someBPRequestVO.getProfileType());
System.out.println("4: " + someBPRequestVO.getEffectiveDate());
when(mockBPService.getProduct(someBPRequestVO)).thenReturn(invalidServiceResponse);
mockTestClassFacade.getTestData(testString);
verify(mockBPService).getProduct(someBPRequestVO);
}
}
System output:
1: null
2: ALL
3: Required - TEST
4: null
What's happening here is that when I run the test the serviceResponse object is null on the line in the CUT marked with //PROBLEM above. My desire is to have that object be populated with my "invalidServiceResponse" object from my test method. Judging from the output of my System.out.println's it appears that my bpRequestVO matches my someBPRequestVO in content.
Could some one show me what I'm missing here?
Thanks for your time!
Instead of creating a equals method in your BPRequestVO class you can create a mock argument with "any(YourObject.class)" like this:
when(mockBPService.getProduct(any(BPRequestVO.class))).thenReturn(invalidServiceResponse);
The instance of BPRequestVO that you use with when() is different than the one used in getTestData().
Unless you override equals(), they will not match.
You should not need to write a custom Matcher if you override equals(). Note the following from the Mockito documentation:
"Custom argument matchers can make the test less readable. Sometimes it's better to implement equals() for arguments that are passed to mocks (Mockito naturally uses equals() for argument matching). This can make the test cleaner."
The problem is in your usage of when().
You submit a reference to a constructed instance; as a result, the mocking will return what you want only if the argument passed to the method is the same reference.
What you want is an argument matcher; something like:
when(mockBPService.getProduct(argThatMatches(someBPRequestVO))
.thenReturn(whatYouWant);
Of course, it requires that you write the argument matcher!
Note that there is a builtin matcher which can do what you want:
when(mockBPService.getProduct(eq(someBPRequestVO))).thenReturn(whatYouWant);
This matcher of course requires that your BPRequestVO class implements equals() (and hashCode() too)!
My problem was that mocked services were defined as final.
The BPRequestVO Object instance used for mocking is different than Instance used while junit execution.
The best way is to configure any instance of the object while mocking
when(mockBPService.getProduct(someBPRequestVO)).thenReturn(invalidServiceResponse);
Can be updated with
when(mockBPService.getProduct(Mockito.any(BPRequestVO.class))).thenReturn(invalidServiceResponse);
My issue was passing null as method arguments doesn't match the when() clause I set up.
e.g.
Car car = mock(Car.class)
when(car.start(anyString()).thenReturn("vroom");
assertEquals("vroom", car.start(null));
This would fail.
assertEquals("vroom", car.start("Now"));
This passes.
My issue was with the instance of the service which is autowired/mockbean had different instance at the Test->given() part and whi lein the execution it had different instance.
This was found by running the test in debug mode and checking each value in the mock stub and execution code. If all the parameters and the mocked instance are same then only the thenReturn() will return the expected value.
In myscenario the mocked instance of the class had multiple implementations and by adding #Qualifier("name") the instance became same in the given() and real execution.
it may also happened in multi-thread case. the mocked object's handler been reset by mockito after the return of #Test method while somewhere(another Thread) still using the mocked object.
as for the situation Thread provided by a pool, you can mock a thread pool instead, executing the Runner.run() in current Thread is proved effective.