Why is Mockito behaving weird with InputStreams? - java

While debugging I came across something incredibly strange using Mockito 1.10. I was hoping someone could explain the behavior perceived here:
When I run the following, my thread hangs and my test never returns. CPU of the Java process created goes astronomical too!
#Test(expected = IOException.class)
public void mockitoWeirdness() throws IOException {
final InputStream mis = mock(InputStream.class);
doThrow(IOException.class).when(mis).read();
ByteStreams.copy(mis, new ByteArrayOutputStream());
}
When I manually stub this method as follows, the expected IOException is thrown:
#Test(expected = IOException.class)
public void nonMockitoExpected() throws IOException {
final InputStream mis = new InputStream() {
#Override
public int read() throws IOException {
throw new IOException();
}
};
ByteStreams.copy(mis, new ByteArrayOutputStream());
}
Any help understanding how and why the mockito method is failing would be fantastic.

If you take a look at the ByteStreams implementation, you can see that the read(buf) method is used.
In your case it returns null because there is no mock definition for it and this causes an endless loop in the copy method.
You may either change the default mock behaviour or manually add a definition for the read(buff) method.

You'll want to set up your mock to call the real methods of InputStream when you haven't stubbed them
final InputStream mis = Mockito.mock(InputStream.class, Mockito.CALLS_REAL_METHODS);
The javadoc states
This implementation can be helpful when working with legacy code. When
this implementation is used, unstubbed methods will delegate to the
real implementation. This is a way to create a partial mock object
that calls real methods by default.
Mockito, by default, mocks everything. The ByteStreams#copy method you used first invokes InputStream#read(byte[]). Since mockito has mocked it, it will return 0 which ByteStreams#copy interprets as "there is more to read from this stream" and keeps reading (infinite loop).
By using Mockito.CALLS_REAL_METHODS, you're telling Mockito to call the actual implementation in InputStream, which will delegate to read(), which you've stubbed to throw an exception.

Related

Mock method with Consumer

I want to mock repository.actionOnFile(String path, Consumer<InputStream> action) in this source:
#Autowired
private FileRepositoryService repository;
public Document getDocument(URL url) {
MutableObject<Document> obj = new MutableObject<>();
Consumer<InputStream> actionOnFile = inputStream -> obj.setValue(getDocument(inputStream));
try {
repository.actionOnFile(url.toExternalForm(), actionOnFile);
} catch (S3FileRepositoryException e) {
throw e.getCause();
}
return obj.getValue();
}
The problem is that the second argument is a lambda expression.
How to mock it with mockito, I need to pass to the accept method the input stream to test it?
I found solution!
doAnswer(ans -> {
Consumer<InputStream> callback = ans.getArgument(1, Consumer.class);
InputStream stream = new ByteArrayInputStream("test".getBytes(StandardCharsets.UTF_8));
callback.accept(stream);
return null;
}).when(repository).actionOnFile(eq("any"), any(Consumer.class));
If you only want to mock the Function argument then the following would work:
Mockito.when(convertStringtoInt(Mockito.any(String.class), Mockito.any(Consumer.class))).then[...]
How to mock it with mockito, I need to pass in accept method test
input stream?
In your case, you want to test the getDocument() method.
So what you need to mock is the dependency of the class under test :
that is the repository field.
actionOnFile.add() more specifically should be mocked.
According to your code, either the method should throw S3FileRepositoryException or it provokes a side effect not visible in the code.
In the exception scenario, you should write something as :
 Mockito.when(fileRepositoryServiceMock.actionOnFile(url.toExternalForm(), actionOnFile)).thenThrow(new S3FileRepositoryException(cause));
And in the successfull, you should just verify that the method is invoked :
 Mockito.verify(fileRepositoryServiceMock).actionOnFile(url.toExternalForm(), actionOnFile));
Mocking a Consumer is really not a big deal.
It is a interface, you can mock any interface with Mockito.
The real issue is actually the Consumer makes not part of the API of the tested method.
It is a local variable.
Besides, it relies on an inputStream field that is not show in the code.
You cannot and have not to mock internal things.
Note that it also relies on a overloaded getDocument() method that is not mocked. So you would need to provide a consistent InputStream if you want to getDocument() that accepts a inputStream doesn't throw an exception.
Long story short : I think that you should either rethink your design to extract the depending processings in another class or write an integration test.

Cannot throw an exception using Mockito

When the method is running I would like to throw an exception (while testing).
I could do few things:
stub(mock.someMethod("some arg")).toThrow(new RuntimeException());
when(mock.someMethod("some arg")).thenThrow(new RuntimeException())
doThrow.....
Usually I create a spy object to call spied method. Using stubbing I can throw an exception. This exception is always monitored in log. More importantly is that the test does not crash because the method where the exception was thrown could catch it and return specific value. However, in the code bellow exception is not thrown (nothing is monitored in the log && return value is true but should be false).
Issues: In this case the exception is not thrown:
DeviceInfoHolder deviceInfoHolder = new DeviceInfoHolder();
/*Create Dummy*/
DeviceInfoHolder mockDeviceInfoHolder = mock (DeviceInfoHolder.class);
DeviceInfoHolderPopulator deviceInfoHolderPopulator = new DeviceInfoHolderPopulator();
/*Set Dummy */
deviceInfoHolderPopulator.setDeviceInfoHolder(mockDeviceInfoHolder);
/*Create spy */
DeviceInfoHolderPopulator spyDeviceInfoHolderPopulator = spy(deviceInfoHolderPopulator);
/*Just exception*/
IllegalArgumentException toThrow = new IllegalArgumentException();
/*Stubbing here*/
stub(spyDeviceInfoHolderPopulator.populateDeviceInfoHolder()).toThrow(toThrow);
/*!!!!!!Should be thrown an exception but it is not!!!!!!*/
boolean returned = spyDeviceInfoHolderPopulator.populateDeviceInfoHolder();
Log.v(tag,"Returned : "+returned);
Creating new answer, because spyDeviceInfoHolderPopulator.populateDeviceInfoHolder(); is your testing method.
One of the basic rules of unit testing is that your shouldn't stub on testing method, because you want to test it's behavior. You may want to stub methods of faked dependencies of test class with Mockito.
So in this case, you probably want to remove the spy, call your testing method and as last stage of your test (that is missing currently), you should verify if logic in testing method was correct.
EDIT:
After last comment it is finally clear to me what logic you are testing.
Let say that your testing object has dependency on some XmlReader. Also immagine that this reader has method called "readXml()" and is used in your testing logic for reading from XML. My test would look like this:
XmlReader xmlReader = mock (XmlReader.class);
mock(xmlReader.readXml()).doThrow(new IllegalArgumentException());
DeviceInfoHolderPopulator deviceInfoHolderPopulator = new DeviceInfoHolderPopulator(xmlReader);
//call testing method
boolean returned = spyDeviceInfoHolderPopulator.populateDeviceInfoHolder();
Assign.assignFalse(returned);
There is slightly different syntax for spy:
doThrow(toThrow).when(spyDeviceInfoHolderPopulator).populateDeviceInfoHolder();
Read more in section "Important gotcha on spying real objects!" here: https://mockito.googlecode.com/svn/tags/latest/javadoc/org/mockito/Mockito.html#13
The creation of spy object is bad here.
The creation should be like
DeviceInfoHolderPopulator spyDeviceInfoHolderPopulator = spy(new DeviceInfoHolderPopulator());
then stub your methods on the spy object.
Reference : API
EDIT:
This is from API.
Sometimes it's impossible or impractical to use Mockito.when(Object) for stubbing spies.
Therefore for spies it is recommended to always
use doReturn|Answer|Throw()|CallRealMethod family of methods for stubbing.
Example:
List list = new LinkedList();
List spy = spy(list);
//Impossible: real method is called so spy.get(0) throws IndexOutOfBoundsException (the list is yet empty)
when(spy.get(0)).thenReturn("foo");
//You have to use doReturn() for stubbing
doReturn("foo").when(spy).get(0);

Why do we throw exceptions and assert it throws exceptions using junit 4 + mockito

I am learning mockito and unit testing.This is my constructor. To test the constructor that it throws jparse exceptions, I have a test method. Why do we generate our own exception and assert it. How is it testing? What are we testing here? Please help!
public ClassA(File file) throws JsonParseException,
JsonMappingException, IOException {
ObjectMapper json= new ObjectMapper();
Map<String, String>> readValue= mapper.readValue(file,
Map.class);
..........
}
#Test(expected = JsonParseException.class)
public void testCorruotionInContent() throws Exception {
ObjectMapper json= Mockito.mock(ObjectMapper.class);
PowerMockito.whenNew(ObjectMapper.class).withNoArguments()
.thenReturn(json);
Mockito.when(
mapper.readValue(Mockito.any(File.class), Mockito.eq(Map.class)))
.thenThrow(new JsonParseException(null, null));
new ClassA(Mockito.mock(File.class));
}
Your test is fragile - as pointed out in comments, you're not testing anything about a concrete implementation, but rather the mock. I'm not even convinced you're testing anything at all.
Remember: you assert against concrete data; you mock what you absolutely need to.
Given that I don't know the full implementation of Constructor, I don't know what it'd take to cause it to fail. I do know, however, there are at least three conditions in which it would fail:
Some kind of IOException - likely the file doesn't exist or can't be read
Some kind of JsonMappingException - the object can't be mapped to JSON
Some kind of JsonParseException - the JSON entity can't be parsed
It is good practice to test all of these conditions, to ensure that your code behaves appropriately when any of these exceptions come up. If you don't expect your code to handle it, then it is acceptable for it to throw these exceptions, and when such a situation occurs in your code, you are verifying that the exception was actually thrown.
But how do we get to that? Let's start off simple.
Suppose we mocked out the instance of the File, and whenever/wherever we decided to read it, we'd get back a string of invalid JSON. That would be a sufficient case to test against - we can't create a file on the host file system (not worth the headache to spin up different files, so simply mocking them out would be acceptable), but we can dictate the data coming from the file.
(In the time I've written this answer, you've changed the class name from Constructor to Class. That's not going to fly - there's already a Class object.)

Mocking a Spy method with Mockito

I am writing a unit test for a FizzConfigurator class that looks like:
public class FizzConfigurator {
public void doFoo(String msg) {
doWidget(msg, Config.ALWAYS);
}
public void doBar(String msg) {
doWidget(msg, Config.NEVER);
}
public void doBuzz(String msg) {
doWidget(msg, Config.SOMETIMES);
}
public void doWidget(String msg, Config cfg) {
// Does a bunch of stuff and hits a database.
}
}
I'd like to write a simple unit test that stubs the doWidget(String,Config) method (so that it doesn't actually fire and hit the database), but that allows me to verify that calling doBuzz(String) ends up executing doWidget. Mockito seems like the right tool for the job here.
public class FizzConfiguratorTest {
#Test
public void callingDoBuzzAlsoCallsDoWidget() {
FizzConfigurator fixture = Mockito.spy(new FizzConfigurator());
Mockito.when(fixture.doWidget(Mockito.anyString(), Config.ALWAYS)).
thenThrow(new RuntimeException());
try {
fixture.doBuzz("This should throw.");
// We should never get here. Calling doBuzz should invoke our
// stubbed doWidget, which throws an exception.
Assert.fail();
} catch(RuntimeException rte) {
return; // Test passed.
}
}
}
This seems like a good gameplan (to me at least). But when I actually go to code it up, I get the following compiler error on the 2nd line inside the test method (the Mockito.when(...) line:
The method when(T) in the type Mockito is not applicable for the arguments (void)
I see that Mockito can't mock a method that returns void. So I ask:
Am I approaching this test setup correctly? Or is there a better, Mockito-recommended, way of testing that doBuzz calls doWidget under the hood? And
What can I do about mocking/stubbing doWidget as it is the most critical method of my entire FizzConfigurator class?
I wouldn't use exceptions to test that, but verifications. And another problem is that you can't use when() with methods returning void.
Here's how I would do it:
FizzConfigurator fixture = Mockito.spy(new FizzConfigurator());
doNothing().when(fixture).doWidget(Mockito.anyString(), Mockito.<Config>any()));
fixture.doBuzz("some string");
Mockito.verify(fixture).doWidget("some string", Config.SOMETIMES);
This isn't a direct answer to the question, but I ran across it when trying to troubleshoot my problem and haven't since found a more relevant question.
If you're trying to stub/mock an object marked as Spy, Mockito only picks up the stubs if they're created using the do...when convention as hinted at by JB Nizet:
doReturn(Set.of(...)).when(mySpy).getSomething(...);
It wasn't being picked up by:
when(mySpy.getSomething(...)).thenReturn(Set.of(...));
Which matches the comment in MockHandlerImpl::handle:
// stubbing voids with doThrow() or doAnswer() style
This is a clear sign that doWidget method should belong to another class which FizzConfigurator would depend on.
In your test, this new dependency would be a mock, and you could easily verify if its method was called with verify.
In my case, for the method I was trying to stub, I was passing in incorrect matchers.
My method signature (for the super class method I was trying to stub): String, Object.
I was passing in:
myMethod("string", Mockito.nullable(ClassType.class)) and getting:
Hints:
1. missing thenReturn()
2. you are trying to stub a final method, which is not supported
3: you are stubbing the behaviour of another mock inside before 'thenReturn' instruction if completed
When using a matcher in another parameter, we also need to use one for the string:
myMethod(eq("string"), Mockito.nullable(ClassType.class))
Hope this helps!

How to mock/test method that returns void, possibly in Mockito

I came across a problem and I can't find an elegant solution.
So the problem is with a mock of Selenium web driver, and I dont know how should I test/mock void methods.
public void clickAndWait(String locator) {
if(isElementPresent(locator) == false) throw some exception;
selenium.clickAndWait(); //a problematic delegating call to selenium
}
So what I am asking is, how to properly test such a method, one test would be for exception being thrown, but how properly make test of that void method I delegate to?
The following code sample from this Mockito documentation illustrates how to mock a void method:
doThrow(new RuntimeException()).when(mockedList).clear();
// following throws RuntimeException:
mockedList.clear();
doAnswer(new Answer<Void>() {
#Override
public Void answer(InvocationOnMock invocation) throws Throwable {
return null;
}
}).when(mock).method((SomeClass) anyObject());
The previous answers have been stressing on doing something (throwing an exception possibly) at every call. This way when you do something like :
doThrow(new RuntimeException()).when(mockedList).clear();
and then call the stubbed service (or logic) like :
mockedList.clear();
it will generate an exception. What if you want to test for a proper functioning of method maybe writing positive test case. Mocking a void returning method for such case could be done by :
doNothing().when(mockedList).clear();
which means that since you stubbed the clear() method for mockedList mock, you can be sure that this method is not going to effect the logic of the unit and still you can check the rest of the flow without generating an exception.
You can also use:
The method Mockito.verify(mock/spy) to check how many times the method has been called.
Or use the argument captor to see/check some parameters passed to the void method.
You can trow an exception on your method call, here is a small example how to do it:
doThrow(new RuntimeException()).when(mockedList).clear();
then you call mockedList.clear(); mocked method will throw an exception.
Or you can count how many times your method was called, here is a small example how to do it:
verify(mockedList, times(1)).clear();
In Java 8 this can be made a little cleaner
doAnswer((i) -> {
// Do stuff with i.getArguments() here
return null;
}).when(*mock*).*method*(*methodArguments*);
The return null; is important and without it the compile will fail with some fairly obscure errors as it won't be able to find a suitable override for doAnswer.

Categories

Resources