0

Here is the source code:

import org.eclipse.jetty.server.Server;

public class WebServer {

    private final Server server;

    public WebServer(Server server) {
        this.server = server;
    }

    public WebServer() {
        this(new Server(1234));
    }

    public void start() {
        try {
            server.start();
        } catch (Exception e) {
            throw new IllegalStateException(format("Could not start server on port '%d'", server.getURI().getPort()), e);
        }
    }

    public void stop() {
        try {
            server.stop();
        } catch (Exception e) {
            throw new IllegalStateException(format("Could not stop server on port '%d'", server.getURI().getPort()), e);
        }
    }
}

The test code:

import org.eclipse.jetty.server.Server;

public class WebServerTest implements WithAssertions {

    private final Server server = mock(Server.class);
    private final WebServer webServer = new WebServer(server);

    @Test
    public void shouldStartJettyServer() throws Exception {
        webServer.start();

        verify(server).start();
    }
}

I want to test the class WebServer, and make sure that it calls method start() from Server dependency(from Jetty library). The source code works and the test is simple and should just check that the method on the mock is called.

But then I am getting back java.lang.NullPointerException when it hits "server.start()".

Looking at the source code of jetty, the start() method is final, throws an exception and comes from a parent class "AbstractLifeCycle" which is inherited by "Server" class.

What am I missing? How can I get this test to work? Is it due to the Exception being thrown by Server.start()? Is it something to do with verifying inherited methods (Although I tried this using some dummy classes and it was not a problem)?

I have done the same code, but instead of using Jetty, i have used an interface, and the tests pass.

public class WebServer {

    private final Server server;

    public WebServer(Server server) {
        this.server = server;
    }

    public void startServer() {
        try {
            this.server.start();
        } catch (Exception e) {
            throw new IllegalStateException(format("Could not startServer server on port '%d'", this.server.getURI().getPort()), e);
        }
    }
}

and interface

public interface Server {
    void start();
    DatagramSocket getURI();
}

and test code

public class WebServerTest {

private final Server server = mock(Server.class);
private final WebServer webServer = new WebServer(server);

@Test
public void blah() throws Exception {
    webServer.startServer();

    verify(server).start();
}
}

So it feels like something internal to how jetty implements start() and/or how mockito mocks the Server class

The stack trace is

java.lang.NullPointerException at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:61) at com.hanfak.greedydb.infrastructure.entrypoints.rest.JettyWebServerTest.shouldStartJettyServer(JettyWebServerTest.java:57) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

cani
  • 210
  • 3
  • 10
  • have you initialized your mocks? – Ulad Dec 01 '18 at 22:38
  • Thanks for response. `private final Server server = mock(Server.class);` This line initializes the mock. I want to verify the method start() is called from the mock. The start() method return type is void, so no behaviour returned thus no when().thenReturn() – cani Dec 02 '18 at 09:51
  • I mean you need to use MockitoJUnitRunner or call MockitoAnnotations.initMocks() explicitly to initalize your mocks. You car read about it here: https://stackoverflow.com/questions/15494926/initialising-mock-objects-mockito – Ulad Dec 02 '18 at 12:02
  • If you're calling `MockitoAnnotations.initMocks` then you're probably trying to force `@Mock` and `@Spy` etc to work, not get a plain mock to work. The above code correctly instantiates mocks. The `MockitoJUnitRunner` would be nicer, but it's not the root of the problem. – Ashley Frieze Dec 03 '18 at 07:32

2 Answers2

0

The root cause of this would appear to be that the logging output of Jetty includes server.getURI().getPort(). Your mock will return null to everything you haven't set up to return. If you added a when(server.getURI()).thenReturn(someUri) to the test setup, this would likely start working.

Ashley Frieze
  • 4,993
  • 2
  • 29
  • 23
0

After investigation. I found that Jetty Server class, inherits the start() method from AbstractLifeCycle class. This start method is declared final. Thus mockito cannot mock this.

I have found that using this version of mockito (ie mocktio 2), see below for maven import

<dependency>
  <groupId>org.mockito</groupId>
  <artifactId>mockito-inline</artifactId>
  <version>2.23.4</version>
  <scope>test</scope>
</dependency>

I can now run the test and it will mock it and the above the test will pass.

Here is the documentation for this library: https://github.com/mockito/mockito/wiki/What%27s-new-in-Mockito-2#unmockable

Lesson: Any method or class that is declared final cannot be mocked.

cani
  • 210
  • 3
  • 10