3

I'm testing this snippet of code

  this.httpClient.start();
  ContentResponse response = this.httpClient.newRequest(Protocol.IMAGES())
                                            .method(HttpMethod.GET)
                                            .send();

  if(response.getStatus() != 200)
    throw new HostIsNotReachableException();

  images = ImageList.fromJson(response.getContentAsString());

  this.httpClient.stop();

using

HttpClient httpClient = mock(HttpClient.class);
Request request = mock(Request.class);
ContentResponse response = mock(ContentResponse.class);
when(httpClient.newRequest(Protocol.IMAGES())).thenReturn(request);
when(request.method(HttpMethod.GET)).thenReturn(request);
when(request.send()).thenReturn(response);
when(response.getStatus()).thenReturn(200);
when(response.getContentAsString()).thenReturn(gson.toJson(images));
...
verify(httpClient, times(1)).start();
verify(httpClient, times(1)).stop();
verify(httpClient, times(1)).newRequest(Protocol.IMAGES());
verify(request, times(1)).method(HttpMethod.GET);
verify(request, times(1)).send();

When start() and/or stop() methods are invoked, the test reports an unexpected NullPointerException. There is something I miss in the mock definition?

This is the log of the exception:

java.lang.NullPointerException
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:61)
at com.meetecho.docker.client.ClientTest.itShouldGetTheImagesList(ClientTest.java:99)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.runTestClass(JUnitTestClassExecuter.java:86)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.java:49)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor.processTestClass(JUnitTestClassProcessor.java:69)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:48)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.messaging.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
at org.gradle.messaging.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:105)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.messaging.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:355)
at org.gradle.internal.concurrent.DefaultExecutorFactory$StoppableExecutorImpl$1.run(DefaultExecutorFactory.java:64)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
helloIAmPau
  • 71
  • 1
  • 2
  • 10

4 Answers4

2

great question, I ran into a very similar problem and this stackoverflow appeared at the top of google results for me.

In my case I was trying to test a code snippet that added my custom Filter to my ApplicationContext, mocking out objects where necessary. I ran into the issue that when my code called filterHolder.start(), (which is in fact the final method start() in AbstractLifeCycle), it would throw a NullPointerException.

To build further off your own self-answer: You can use Powermock to overcome the issue of Mockito not being able to mock final methods.

With Powermock libraries included in your project, enable the Powermock runner and flag the AbstractLifeCycle.class class for bytecode manipulation:

@PrepareForTest(AbstractLifeCycle.class)
@RunWith(PowerMockRunner.class)
public class MyTestClass extends Assert {

Then, mock out the initialize() and start() methods:

doAnswer(new DoesNothing()).when(holder).initialize();
doAnswer(new DoesNothing()).when(holder).start();

and it should work fine.

fragorl
  • 1,698
  • 15
  • 19
1

I auto-reply to my answer because no one has proposed the right solution.

I realized that start/stop methods of the jetty-client class are final. Mockito does not mock final methods, but executes their real implementation.

How I resolved this issue right now? By starting to use jersey-client.

helloIAmPau
  • 71
  • 1
  • 2
  • 10
0

It looks like the object httpClient is null. You are actually mocking httpClient but the mock is never received in the actual code that you are testing.

Even if you inject the object or you are instantiating it doing new HttpClient in the tested code you'll have to add a setter method in order to set the httpClient object from outside. Of course this method would be only used for testing purposes, but it is a common practice.

In other words, assuming that your code is in class A, and the method that the method that contains the code to be testes is methodA, you should have something similar to:

class A {
    private HttpClient httpClient=null; 

    public methodA {
        if (this.httpClient==null) this.httpClient = new HttpClient(...);
        <YOUR CODE>
    }

    public void setHttpClient(HttpClient httpClient) {
        this.httpClient = httpClient;
    }
    ...
 }

And in the test you should call the method setHttpClient passing as a parameter the mock of HttpClient that you create at the first line.

ipinyol
  • 336
  • 2
  • 12
  • I set the httpClient object using `Client uut = new Client(); Field f = Client.class.getDeclaredField("httpClient"); f.setAccessible(true); f.set(uut, httpClient);` I've omitted it in the code above for the sake of brevity. – helloIAmPau Aug 06 '14 at 10:08
  • good! you still can follow the same approach. Just make sure not to execute the code if httpClient is different than null, because if this is the case, it means that you have set manually httpClient from the unit test. So, basically if you replace the piece of code **this.httpClient = new HttpClient(...);** provided in the asnwer with by the one you just put, it should work properly. – ipinyol Aug 06 '14 at 16:14
  • Considering you have a member variable `this.httpClient` you can remove your need for reflection by injecting a `new HttpClient()` as a constructor argument. It's explicit about what your class needs, and your tests are cleaner. You may also need to consider [multi-threading issues](http://stackoverflow.com/questions/5455635/how-to-use-httpclient-with-multithreaded-operation) if you are holding on to the HttpClient reference. – Brad Aug 10 '14 at 11:34
0

In newer Mockito versions you can do the following as suggested in https://www.baeldung.com/mockito-final:

Before Mockito can be used for mocking final classes and methods, it needs to be configured.

We need to add a text file to the project's src/test/resources/mockito-extensions directory named org.mockito.plugins.MockMaker and add a single line of text:

mock-maker-inline

Mockito checks the extensions directory for configuration files when it is loaded. This file enables the mocking of final methods and classes.

Community
  • 1
  • 1
Danylo Zatorsky
  • 5,856
  • 2
  • 25
  • 49