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!
Related
I am trying to mock a static method render of the TemplateRendererUtil class to return null when testing. Not sure if this is the right approach, the method I'm trying to test calls this class, but I just want it to essentially skip this.
Getting a compilation error here:
try (MockedStatic<TemplateRendererUtil> mockedStatic = Mockito.mockStatic(TemplateRendererUtil.class)){
// java: incompatible types: incompatible parameter types in method reference
mockedStatic.when(TemplateRendererUtil::render).thenReturn(null);
}
Class being referenced:
public class TemplateRendererUtil {
private static final ResponseTemplateRenderer RENDERER = ResponseTemplateRenderer.getInstance();
private TemplateRendererUtil() {
}
public static void render(HttpServletRequest req, HttpServletResponse resp, String templateFile, Map<String, Object> params) throws TemplateRendererUtilException {
try {
RENDERER.render(req, resp, templateFile, params);
} catch (ResponseTemplateRendererException var5) {
throw new TemplateRendererUtilException(var5);
}
}
}
Stack trace of at Mockito.mockStatic:
java.lang.InternalError: class redefinition failed: invalid class
at java.instrument/sun.instrument.InstrumentationImpl.retransformClasses0(Native Method)
at java.instrument/sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:167)
at org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.triggerRetransformation(InlineBytecodeGenerator.java:280)
at org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.mockClassStatic(InlineBytecodeGenerator.java:225)
at org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator.mockClassStatic(TypeCachingBytecodeGenerator.java:63)
at org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker.createStaticMock(InlineDelegateByteBuddyMockMaker.java:560)
at org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker.createStaticMock(InlineByteBuddyMockMaker.java:83)
at org.mockito.internal.util.MockUtil.createStaticMock(MockUtil.java:147)
at org.mockito.internal.MockitoCore.mockStatic(MockitoCore.java:142)
at org.mockito.Mockito.mockStatic(Mockito.java:2181)
at org.mockito.Mockito.mockStatic(Mockito.java:2118)
...
As I see, render method is not returning anything so you can do like this
try (MockedStatic<TemplateRendererUtil> mockedStatic = Mockito.mockStatic(TemplateRendererUtil.class)){
doNothing().when(TemplateRendererUtil.render(Mockito.any(), Mockito.any(), Mockito.anyString(), Mockito.anyMap()));
}
There are actually two problems with your code.
First is the fact that you did not provide the code for the method TemplateRendererUtil.render() (without arguments) and you're using such a method's reference as a parameter passed to when(). You're not referencing the method with 4 arguments that you've shown in your test code.
The second problem is the fact that you're trying to return null from a method that does not return anything (void type).
The correct code would be:
try (MockedStatic<TemplateRendererUtil> mockedStatic = Mockito.mockStatic(TemplateRendererUtil.class)) {
mockedStatic.when(() -> TemplateRendererUtil.render(any(), any(), anyString(), anyMap()))
.thenAnswer(inv -> null);
// rest of the test
}
Instead of any... matchers you could use more appropriate ones if needed.
I've reproduced the problem and tested the solution - you can find it here on GitHub. The test passes. The mocked method was simplified to use int and long, but the mechanism stays the same.
Trying to write a test that will call my method, when that method makes a call to another method we will throw a custom exception i have made. Here i have simplified it all
2 functions
public MyJsonResponse hello() {
MyJsonResponse response = new MyJsonResponse();
response.setErrorMessage("1");
response.setStatus("some status");
response.setData("1");
response.setHttpResponse(200);
try{
hi();
return response;
}catch (MyServiceException e) {
response.setErrorMessage(e.getMessage());
response.setStatus("error creating");
response.setData("2");
response.setHttpResponse(e.getResponseStatus());
return response;
}
}
public String hi() throws MyServiceException{
LOG.error("Exception");
return "yea";
}
The test I have written is this
#Test
public void myTest() throws Exception {
given(service.hi()).willAnswer( invocation -> { throw new MyServiceException("abc msg",511); });
MyJsonResponse actual = service.hello();
Assert.assertNotNull(actual);
assertEquals(511, actual.getHttpResponse());
}
But unfortunately the result is as follows
java.lang.AssertionError:
Expected :511
Actual :200
Please, be sure that you are using a spy as you want to use the actual code for some methods of your mocked service and just stubbing specific methods of it. Please, see for instance this related SO question about the subject.
Also, consider modifying your test definition to use willThrow instead of willAnswer: as pointed out by #eis, you can still use the later, but the former is more straightforward.
Your code will look similar to this:
#Test
public void myTest() throws Exception {
MyService service = spy(MyService.class);
willThrow(new MyServiceException("abc msg",511))
.given(service)
.hi()
;
// As pointed out by #eis, you can still use willAnswer
// willAnswer(
// invocation -> { throw new MyServiceException("abc msg",511);}
// )
// .given(service)
// .hi()
// ;
MyJsonResponse actual = service.hello();
Assert.assertNotNull(actual);
assertEquals(511, actual.getHttpResponse());
}
regarding what you explain and what your code look like, I am not sure if I have well understood.
Thus, if you want that, your hi() : function throws an exception.
You have to make it first throws an exception. Take a look at code below!
public String hi() throws MyServiceException{
/*LOG.error("Exception");//No don't just log, throw a real exception as below*/
throw new MyServiceException("text here, if your constructor support it or nothing otherwise")
/*return "yea";//Nothing to return? we have just break the code by throwing the exception above*/
}
After that, please be very sure that your 'MyServiceException.getHttpResponse()' will really return 511
For this test to make sense, your hi() call should be done calling another service that you stub/mock in your test class. You're not doing that, so this approach won't work.
You wrote "the real method that hi represents does a lot", so it's about time you extract that to another service.
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'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);
}
I have the following object which I want to test:
public class MyObject {
#Inject
Downloader downloader;
public List<String> readFiles(String[] fileNames) {
List<String> files = new LinkedList<>();
for (String fileName : fileNames) {
try {
files.add(downloader.download(fileName));
} catch (IOException e) {
files.add("NA");
}
}
return files;
}
}
This is my test:
#UseModules(mockTest.MyTestModule.class)
#RunWith(JukitoRunner.class)
public class mockTest {
#Inject Downloader downloader;
#Inject MyObject myObject;
private final String[] FILE_NAMES = new String[] {"fail", "fail", "testFile"};
private final List<String> EXPECTED_FILES = Arrays.asList("NA", "NA", "mockContent");
#Test
public void testException() throws IOException {
when(downloader.download(anyString()))
.thenThrow(new IOException());
when(downloader.download("testFile"))
.thenReturn("mockContent");
assertThat(myObject.readFiles(FILE_NAMES))
.isEqualTo(EXPECTED_FILES);
}
public static final class MyTestModule extends TestModule {
#Override
protected void configureTest() {
bindMock(Downloader.class).in(TestSingleton.class);
}
}
}
I am overwriting the anyString() matcher for a specific argument. I am stubbing the download() method so that it returns a value for a specific argument and otherwise throws an IOException which gets handled by MyObject.readFiles.
The weird thing here is that the second stub (downloader.download("testFile")) throws the IOException set in the first stub (downloader.download(anyString())). I have validated that by throwing a different exception in my first stub.
Can someone explain me why the exception is thrown when adding an additional stub? I thought that creating a stub does not call the method/other stubs.
The problem is that when you write
when(downloader.download("testFile")).thenReturn("mockContent");
the first thing to be called is downloader.download, which you've already stubbed to throw an exception.
The solution is to use the slightly more versatile stubbing syntax that Mockito provides. This syntax has the advantage that it doesn't call the actual method when stubbing.
doThrow(IOException.class).when(downloader).download(anyString());
doReturn("mock content").when(downloader).download("test file");
I have listed other advantages of this second syntax, in my answer here
I thought that creating a stub does not call the method/other stubs.
This assumption is wrong, because stubbing is calling the mocks methods. Your test methods are still plain java!
Since stubbing for anyString will overwrite stubbing for any specific string you will either have to write two tests or stub for two specific arguments:
when(downloader.download("fail")).thenThrow(new IOException());
when(downloader.download("testFile")).thenReturn("mockContent");
Mockito is a very sophisticated piece of code that tries its best so that you can write
when(downloader.download(anyString())).thenThrow(new IOException());
which means “when the downloaders mock download method is called with anyString argument thenThrow an IOException” (i.e. it can be read from left to right).
However, since the code is still plain java, the call sequence actually is:
String s1 = anyString(); // 1
String s2 = downloader.download(s1); // 2
when(s2).thenThrow(new IOException()); // 3
Behind the scenes, Mockito needs to do this:
register an ArgumentMatcher for any String argument
register a method call download on the downloader mock where the argument is defined by the previously registered ArgumentMatcher
register an action for the previously registered method call on a mock
If you now call
... downloader.download("testFile") ...
the downloader mock checks whether there is an action register for "testFile" (there is, since there is already an action for any String) and accordingly throws the IOException.
Your 2nd mock statement is getting overriden by the first mock statement (because both mock statements are passing a String argument). If you want to cover try as well as catch back through your mock test then write 2 different test cases.