0
votes

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)

2
have you initialized your mocks?vlad324
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
I mean you need to use MockitoJUnitRunner or call MockitoAnnotations.initMocks() explicitly to initalize your mocks. You car read about it here: stackoverflow.com/questions/15494926/…vlad324
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

2 Answers

0
votes

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.

0
votes

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.