I'm trying to mock a method that creates a local variable, tries something, and does logging if an exception is thrown. Here's a snippet of code:
public void myFunction() {
//Some stuff
try {
new File("foo").getAbsoluteFile();
} catch (SecurityException e) {
//Do some logging
}
}
I'd like to mock this logging behavior using JMockit (using version 1.8 if it matters). So I created the following test:
#Test(expected=SecurityException.class)
public void testAbsoluteFile(
#Injectable final File file
) throws IOException {
new Expectations(File.class){{
new File(anyString);
result = file;
file.getAbsoluteFile();
result = new SecurityException();
}};
myFunction();
}
The trouble is that this seems to give me a NullPointerException on the inner workings of File.getAbsoluteFile(), which I find absolutely bizarre:
java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NullPointerException>
Caused by: java.lang.NullPointerException
at java.io.Win32FileSystem.slashify(Win32FileSystem.java:56)
at java.io.Win32FileSystem.resolve(Win32FileSystem.java:330)
at java.io.File.getAbsolutePath(File.java:556)
at java.io.File.getAbsoluteFile(File.java:572)
at com.vue.rescoreservice.ExpectationTest.myFunction(ExpectationTest.java:39)
at com.vue.rescoreservice.ExpectationTest.testAbsoluteFile(ExpectationTest.java:33)
This seems really bizarre as it's saying that a local variable in the Win32FileSystem class (an internal class not in the typical Java API) is throwing a NullPointerException, when it never did before.
The lines that are in the stack trace are as follows:
//In myFunction()
new File("foo").getAbsoluteFile();
and
//In testAbsoluteFile()
myFunction();
Why is this happening? And how can I make it so that JMockit does not throw a NullPointerException on local variables of internal classes?
That issue has been fixed in the latest jMockit version (1.14). If you don't want to migrate now, it's possible to fix the test in 1.8 (see the code below).
In this case, the #Injectable isn't necessary. The constructor of File is mocked for the Expectations and that requires the mocking of the class itself instead of a single instance. In that case, the behavior is equivalent to #Mocked (but File will be partially mocked according to the calls in the Expectations block).
The catch clause in myFunction needs to rethrow the SecurityException to allow the test to pass.
#Test(expected=SecurityException.class)
public void testAbsoluteFile() throws IOException {
new Expectations(File.class) {{
File file = new File(anyString);
file.getAbsoluteFile();
result = new SecurityException();
}};
myFunction();
}
If you prefer to declare the mock as a parameter, it will also work, but File won't be partially mocked (all the methods will be mocked).
#Test(expected=SecurityException.class)
public void testAbsoluteFile(#Mocked final File file) throws IOException {
new Expectations(){{
new File(anyString);
file.getAbsoluteFile();
result = new SecurityException();
}};
myFunction();
}
An update on #Marc-André answer.
In my case with JMockit 1.25 I had to define the file variable outside of the expectations leaving it like:
#Test(expected=SecurityException.class)
public void testAbsoluteFile() throws IOException {
File file = new File("");
new Expectations(File.class) {{
file.getAbsoluteFile();
result = new SecurityException();
}};
myFunction();
}
Maybe it was a change on JMockit prior to the answer.
Related
I'm trying to write unit test for a case when an exception is thrown but somehow it is throwing null instead of exception.
The service call that I'm trying to mock.
private List<Vertex> getVertexList(final String vertexId, GraphTraversalSource graphTraversalSource, final int indexToLoop) {
return graphTraversalSource.V(vertexId).repeat(in().dedup().simplePath()).until(loops().is(indexToLoop)).toList();
}
I wrote the following to mock to throw Exception
#Mock(answer = RETURNS_DEEP_STUBS)
private GraphTraversalSource gts;
Mockito.when(gts.V(anyString()).repeat(any()).until((Predicate<Traverser<Vertex>>) any()).toList()).thenThrow(Exception.class);
Is there any way to mock this so that it throws exception? Thanks in advance.
Assuming your gts and a call to MockitoAnnotations.initMocks(this) in the #Before of the test, this style worked for me:
GraphTraversal v = mock(GraphTraversal.class);
GraphTraversal repeat = mock(GraphTraversal.class);
GraphTraversal until = mock(GraphTraversal.class);
when(gts.V(anyString())).thenReturn(v);
when(v.repeat(any())).thenReturn(repeat);
when(repeat.until((Predicate<Traverser<Vertex>>) any())).thenReturn(until);
when(until.toList()).thenThrow(RuntimeException.class);
gts.V("test-id").repeat(out()).until(__.loops().is(1)).toList();
Depending on what you are doing you might consider avoiding the mock and just throwing an exception in the traversal itself:
GraphTraversalSource g = EmptyGraph.instance().traversal();
g.inject("test-id").sideEffect(x -> {
throw new RuntimeException();
}).toList();
Obviously, that's a little different from what your mock is doing and does require the traversal to actually have data passing through it (hence my use of inject() to start the traversal rather than V() as g is bound to an EmptyGraph in this case.
I am assigned to add unit test code coverage to a 15 years old legacy project which is not using IoC and 0 unit test. I am not allowed to refactor the code since it works perfect fine on production, management does not want other teams get involved for refactoring such as QA testing, etc.
Service class has a performService method has following code
public void performService(requestMessage, responseMessage) {
UserAccount userAccount = requestMessage.getUserAccount();
GroupAccount groupAccount = requestMessage.getGroupAccount();
Type type = requestMessage.getType();
StaticServiceCall.enroll(userAccount, groupAccount, type);
response.setStatus(Status.SUCCESS);
}
This StaticServiceCall.enroll method is calling remote service. My unit test is
#RunWith(PowerMockRunner.class)
#PrepareForTest(StaticServiceCall.class)
public class EnrollmentServiceTest {
#Test
public void testPerformService() {
mockStatic(StaticServiceCall.class);
doNothing().when(StaticServiceCall.enroll(any(UserAccount.class), any(GroupAccount.class), any(Type.class)));
service.performService(requestMessage, responseMessage);
assertEquals("Enrollment should be success, but not", Status.SUCCESS, response.getStatus);
}
Eclipse complains with The method when(T) in the type Stubber is not applicable for the arguments (void)
Eclipse stops complain if test code change to
mockStatic(StaticServiceCall.class);
doNothing().when(StaticServiceCall.class);
StaticServiceCall.enroll(any(UserAccount.class), any(GroupAccount.class), any(Type.class));
service.performService(requestMessage, responseMessage);
assertEquals("Enrollment should be success, but not", Status.SUCCESS, response.getStatus);
Test case failed with UnfinishedStubbingException. I am using powermock 1.6.6
There is a misconception on your end. You think that you need to say that doNothing() should do nothing.
That is not necessary! As these lines
#PrepareForTest(StaticServiceCall.class) ... and
mockStatic(StaticServiceCall.class);
are sufficient already.
You want to prevent the "real" content of that static method to run when the method is invoked during your test. And that is what mockStatic() is doing.
In other words: as soon as you use mockStatic() the complete implementation of the real class is wiped. You only need to use when/then/doReturn/doThrow in case you want to happen something else than nothing.
Meaning: just remove that whole doNothing() line!
#GhostCat - Thank you for your answer, it solved problem, my misconception is coming from this test case
#Test
public void testEnrollmentServiceSuccess() {
RequestMessage requestMessage = new RequestMessage();
requestMessage.setName("ENROLL");
ResponseMessage responseMessage = new ResponseMessage();
EnrollmentService mockService = mock(EnrollmentService.class);
mockService.performService(any(RequestMessage.class), any(ResponseMessage.class));
mockStatic(ClientManager.class);
when(ClientManager.isAuthenticated()).thenReturn(true);
ServiceImpl service = new ServiceImpl();
service.performService(requestMessage, responseMessage);
verify(mockService).performService(any(RequestMessage.class), any(ResponseMessage.class));
}
Here is the code snippet of ServiceImpl class based name of the request message calling different service class
public void performService(RequestMessage request, ResponseMessage response) {
try {
if (request == null) {
throw new InvalidRequestFormatException("null message");
}
if (!ClientManager.isAuthenticated()) {
throw new ServiceFailureException("not authenticated");
}
// main switch for known services
if ("ENROLL".equals(request.getName())) {
service = new EnrollmentService();
service.performService(request, response);
} else if ("VALIDATE".equals(request.getName())) {
...
Although the test passed,real implementation in EnrollmentService got called and exceptions thrown due to barebone RequestMessage object, then I googled out doNothing, thanks again for your clarification
I recently saw some Mockito 1.9.5 code that worked like this:
MyObject myObject = new MyObject();
...
Mockito.when(myObject.someMethod()).thenReturn("bogus");
Since myObject is not a mock object, but is an instance of a non-mocked class, I was surprised this compiled and ran without failing the unit test. I expected I would get a failure saying something like "You asked me to set up an expectation on a non-mock object, and I expected to set expectations only on mock objects."
Why doesn't this code cause a test failure?
Update: adding more code that is necessary to actually replicate the behavior I find confusing. These examples fully illustrate my question. The following code behaves as I expected--when I run this test the test fails with a message that
when() requires an argument which has to be 'a method call on a mock'.
public class AnotherObject{
public String doSomething(){
return "did something";
};
}
public class MyObject{
private AnotherObject anotherObject = new AnotherObject();
public void setAnotherObject(AnotherObject anotherObject) {
this.anotherObject = anotherObject;
}
public String someMethod(){
return anotherObject.doSomething();
}
}
#Test
public void WhyDoesWhenWorkOnNonMock() throws Exception {
MyObject myObject = new MyObject();
Mockito.when(myObject.someMethod()).thenReturn("bogus");
}
Now if I add a couple specific lines to this contrived test, the test no longer fails even though I expected the same failure and same message as before:
public class AnotherObject{
public String doSomething(){
return "did something";
};
}
public class MyObject{
private AnotherObject anotherObject = new AnotherObject();
public void setAnotherObject(AnotherObject anotherObject) {
this.anotherObject = anotherObject;
}
public String someMethod(){
return anotherObject.doSomething();
}
}
#Test
public void WhyDoesWhenWorkOnNonMock() throws Exception {
MyObject myObject = new MyObject();
AnotherObject mockAnotherObject = Mockito.mock(AnotherObject.class);
myObject.setAnotherObject(mockAnotherObject);
Mockito.when(myObject.someMethod()).thenReturn("bogus");
}
By incredible and fragile coincidence, probably, unless myObject was actually set to be a spy.
Mockito allows for the creation of a "spy" of a real object:
MyObject myObject = spy(new MyObject());
Mockito.when(myObject.someMethod()).thenReturn("something");
// myObject is actually a duplicate of myObject, where all the fields are copied
// and the methods overridden. By default, Mockito silently records interactions.
myObject.foo(1);
verify(myObject).foo(anyInt());
// You can stub in a similar way, though doReturn is preferred over thenReturn
// to avoid calling the actual method in question.
doReturn(42).when(myObject).bar();
assertEquals(42, myObject.bar());
Barring that, this code is probably not working the way it looks like it should. when's parameter is meaningless, and is sugar meant to hide that the mocked interaction is the most recent method call to a mock. For example:
SomeObject thisIsAMock = mock(SomeObject.class);
OtherObject notAMock = new OtherObject();
thisIsAMock.methodOne();
Mockito.when(notAMock.someOtherMethod()).thenReturn("bar");
// Because notAMock isn't a mock, Mockito can't see it, so the stubbed interaction
// is the call to methodOne above. Now methodOne will try to return "bar",
// even if it isn't supposed to return a String at all!
Mismatches like this can be easy sources of ClassCastException, InvalidUseOfMatchersException, and other bizarre errors. It's also possible that your real MyObject class takes a mock as a parameter, and that last interaction is with a mock that someMethod interacts with.
Your edit confirms my suspicions. As far as Java is concerned, it needs to evaluate the parameter to when before it can invoke when, so your test calls someMethod (real). Mockito can't see that—it can only take action when you interact with one of its mocks—so in your first example it sees zero interactions with mocks and thus it fails. In your second example your someMethod calls doSomething, which Mockito can see, so it returns the default value (null) and marks that as the most recent method call. Then the call to when(null) happens, Mockito ignores the parameter (null) and refers to the most recently called method (doSomething), and stubs that to return "bogus" from that point on.
You can see that by adding this assertion to your test, even though you never stubbed it explicitly:
assertEquals("bogus", mockAnotherObject.doSomething());
For an additional reference, I wrote a separate SO answer on Mockito matchers, for which the implementation details might be useful. See steps 5 and 6 for an expanded view of a similar problem.
I've been working with JMockit and its admittedly steep learning curve. I'm pretty new with mocking in general so please excuse the ignorance.
I'm trying to mock out the Logger interface so that I can verify the catch statement is working correctly. Call this an exercise in understanding how JMockit works. The implementing class for the Logger interface is Log4jLoggerAdapter so I thought if I passed an instance of that into my Expectations() block, JMockit would use dynamic partial mocking and "see" my logger statement. Instead, I get the following error:
mockit.internal.MissingInvocation: Missing invocation of: org.slf4j.impl.Log4jLoggerAdapter#error(String msg, Throwable t)
The Class Being Tested
public class MyLoggedClass {
private static final Logger LOGGER = LoggerFactory.getLogger(MyLoggedClass.class);
... // Other variables
#Override
public void connect() {
String info = getServiceInfo();
try {
connector = MyConnectionFactory.connect(info);
} catch (Exception e) {
LOGGER.error("Exception connecting to your service with: " + info, e);
}
}
... // Other methods
}
My #Test
public class MyLoggedClassTest {
#Tested
MyLoggedClass myLoggedClass;
#Test
public void myLoggingTest(#Mocked final Log4jLoggerAdapter logger){
new Expectations(MyConnectionFactory.class, logger){{
MyConnectionFactory.connect(anyString);
result = new Exception();
logger.error(anyString, (Throwable)any);
}};
myLoggedClass.connect();
}
I'd detail the other approaches I've tried but this page would turn into a book. This is my best approach. Any ideas?
* Update * (yes, that was quick)
I changed #Mocked to #Cascading and removed the logger field from my Expectations signature and it worked. I don't understand why. Can someone please provide an explanation? Fumbling about until you stumble on something that works but you don't understand is not a recipe for success. See below:
#Test
public void myLoggingTest(#Cascading final Log4jLoggerAdapter logger){
new Expectations(MyConnectionFactory.class){{
MyConnectionFactory.connect(anyString);
result = new Exception();
logger.error(anyString, (Throwable)any);
}};
myLoggedClass.connect();
}
No need for partial mocking in this case, just mock MyConnectionFactory in the usual way. The only tricky part is how to mock the class that implements the Logger interface, considering that it's instantiated from a static class initializer. As it happens, there is a feature in the mocking API for that (using JMockit 1.14):
public class MyLoggedClassTest
{
#Tested MyLoggedClass myLoggedClass;
#Test
public void myLoggingTest(
#Mocked MyConnectionFactory conFac,
#Capturing final Logger logger)
{
new Expectations() {{
MyConnectionFactory.connect(anyString);
result = new Exception();
}};
myLoggedClass.connect();
new Verifications() {{
logger.error(anyString, (Throwable)any);
}};
}
}
With a #Capturing mocked type, any implementation class will get mocked, so the test doesn't need to know about Log4jLoggerAdapter.
As i just started Mockito, I have below method that i want to test. In my YouTubeChannelImporter.java file there are both methods : readJsonFromUrl(String url) and readAll(rd)
public JSONObject readJsonFromUrl(String url) throws IOException,
JSONException {
InputStream is = new URL(url).openStream();
try {
BufferedReader rd = new BufferedReader(new InputStreamReader(is,
Charset.forName("UTF-8")));
String jsonText = readAll(rd);
JSONObject json = new JSONObject(jsonText);
return json;
} finally {
is.close();
}
}
I started with some rough idea.Below is the sample test case.
#Test
public void readJsonFromUrl() throws IOException, JSONException {
String urlTest="http://google.com/api";
YouTubeChannelImporter mockYoutube = mock(YouTubeChannelImporter.class);
YouTubeChannelImporter tubeChannelImporter =new YouTubeChannelImporter();
// URL url=new URL(urlTest);
// InputStream inputStream = mock(InputStream.class);
// when(url.openStream()).thenReturn(inputStream);
BufferedReader rd=mock(BufferedReader.class);
when(mockYoutube.readAll(rd)).thenReturn("{\"kind\":\"you\"}");
String jsonText="{\"kind\":\"you\"}";
JSONObject object=new JSONObject(jsonText);
assertEquals("{\"kind\":\"you\"}",tubeChannelImporter.readJsonFromUrl(urlTest).toString());
}
My issue is when i will test the function readJsonFromUrl(String url) then readAll(rd) should not execute. Rather mock should be in action here. I am aware this issue is because tubeChannelImporter.readJsonFromUrl(urlTest).toString() . Looking for any other way to achieve my goal.
Thanks
You're mocking one instance of your class, and then executing another - as you noticed, this does not achieve the intended result.
I think the easiest way of testing your method is with spying ("partial mocking"). With this technique, you'll have an actual instance of your class with only a single method, readAll mocked out:
#Test
public void readJsonFromUrl() throws IOException, JSONException {
// Set up the spied instance:
YouTubeChannelImporter tubeChannelImporter = spy(new YouTubeChannelImporter());
doReturn("{\"kind\":\"you\"}").when(tubeChannelImporter).readAll(any(Reader.class));
// Test the execution
String urlTest="http://google.com/api";
String jsonText="{\"kind\":\"you\"}";
JSONObject object=new JSONObject(jsonText);
assertEquals("{\"kind\":\"you\"}",tubeChannelImporter.readJsonFromUrl(urlTest).toString());
}
Personally, for a test like this where you have to read a file and there's not much else going on, I would read an actual file rather than mock out a BufferedReader. Then you just have to Assert on the JSONObject output from the call to readJsonFromUrl().
Your problems stem from trying to test against the private method readAll(). Try to test only your public methods in your tests (e.g. readJsonFromUrl()) and you'll find you have to jump through less hoops in your test code.
If your class already implements an Interface, then just write tests against the Interface.
If you find that you have a lot of private methods, perhaps your class is responsible for doing too much work, so factor methods out into their own class(es). Then you can test them in isolation.
If you have a maven project, the place to put your test files, to read from, is in /src/test/resources