13

I'm using the OkHttp MockWebServer to mock my server responses for unit tests.

It works great for the first test, but on the 2nd test my client fails with:

Failed to connect to localhost/0:0:0:0:0:0:0:1:63631

This happens even if the 2nd test is exactly the same as the 1st one.
Here's what I'm doing:

@RunWith(RobolectricTestRunner.class)
@Config(shadows = MyClassTest.MyNetworkSecurityPolicy.class,
        manifest = "src/main/AndroidManifest.xml",
        constants = BuildConfig.class,
        sdk = 16)
public class MyClassTest {
    private MockWebServer mockServer;
    private MyServerApi serverApi;

    @Before
    public void setUp() throws Exception {
        System.out.println("\ntest start");
        this.mockServer = new MockWebServer();
        this.mockServer.start();

        this.serverApi = new MyServerApi(this.mockServer.url("/").toString());
    }

    @Test
    public void testOne() throws Exception {
        final String responseBody = // read response from file
        this.mockServer.enqueue(new MockResponse().setResponseCode(200).setBody(responseBody));

        final Waiter waiter = new Waiter();
        this.serverApi.getData("some_id", new Callback<MyResponseData> {
            @Override
            public void onResponse(final Call<MyResponseData> call, final Response<MyResponseData> response) {
                waiter.assertEquals("some_value", response.body().getValue());
                waiter.resume();
            }

            @Override
            public void onFailure(final Call<T> call, final Throwable error) {
                waiter.fail(error);
            }
        });

        waiter.await();

        final RecordedRequest recordedRequest = this.mockServer.takeRequest();
        assertEquals("GET", recordedRequest.getMethod());
    }

    @Test
    public void testTwo() throws Exception {
        final String responseBody = // read response from file
        this.mockServer.enqueue(new MockResponse().setResponseCode(200).setBody(responseBody));

        final Waiter waiter = new Waiter();
        this.serverApi.getData("some_id", new Callback<MyResponseData> {
            @Override
            public void onResponse(final Call<MyResponseData> call, final Response<MyResponseData> response) {
                waiter.assertEquals("some_value", response.body().getValue());
                waiter.resume();
            }

            @Override
            public void onFailure(final Call<T> call, final Throwable error) {
                waiter.fail(error);
            }
        });

        waiter.await();

        final RecordedRequest recordedRequest = this.mockServer.takeRequest();
        assertEquals("GET", recordedRequest.getMethod());
    }

    @After
    public void tearDown() throws Exception {
        System.out.println("test end\n");
        this.mockServer.shutdown();
    }

    @Implements(NetworkSecurityPolicy.class)
    public static class MyNetworkSecurityPolicy {
        @Implementation
        public static NetworkSecurityPolicy getInstance() {
            try {
                Class<?> shadow = MyNetworkSecurityPolicy.class.forName("android.security.NetworkSecurityPolicy");
                return (NetworkSecurityPolicy) shadow.newInstance();
            } catch (Exception e) {
                throw new AssertionError();
            }
        }

        @Implementation
        public boolean isCleartextTrafficPermitted() {
            return true;
        }
    }
}

The first test passes as it should, but the second one fails with the message I wrote above.
The output in the console is:

test start
okhttp3.mockwebserver.MockWebServer$3 execute
INFO: MockWebServer[63631] starting to accept connections
WARNING: no system properties value for gsm.sim.operator.alpha
okhttp3.mockwebserver.MockWebServer$4 processOneRequest
INFO: MockWebServer[63631] received request: GET REQUEST_PATH HTTP/1.1 and responded: HTTP/1.1 200 OK
okhttp3.mockwebserver.MockWebServer$3 acceptConnections
test end

INFO: MockWebServer[63631] done accepting connections: Socket closed

test start
okhttp3.mockwebserver.MockWebServer$3 execute
INFO: MockWebServer[63649] starting to accept connections
okhttp3.mockwebserver.MockWebServer$3 acceptConnections
INFO: MockWebServer[63649] done accepting connections: Socket closed
on error: Failed to connect to localhost/0:0:0:0:0:0:0:1:63631
test end

(The Waiter object thing is from the concurrentunit lib)

Any idea why this happens?

Nitzan Tomer
  • 155,636
  • 47
  • 315
  • 299
  • How did you solve this issue, I also have two methods in the same class which test API valid and invalid response, when I run it individually both test methods work fine. But when I run the whole class one of them works and the other gives `Failed to connect to localhost/127.0.0.1:58688` error. How did you solve this issue? – Kavita Patil Oct 08 '19 at 17:49
  • @Kavita_p I don't remember, more than 3 years passed since. I marked an answer as the accepted one, so I guess that one helped. – Nitzan Tomer Oct 08 '19 at 18:46
  • 2
    Hi @Nitzan Tomer , I solved the above issue by setting `mockWebServer.start(PORT_NUMBER)` constant port number to my mockWebServer and I am using `mockWebServer.shutdown()` in `@After method` to shut down it once I am done with test cases – Kavita Patil Oct 14 '19 at 20:24
  • I solved the same issue by keeping a constant port number. As not providing a constant port number probably starts a new mock web server on random ports, our retrofit instance in android (or whatever you are using for your HTTP requests) fails to send requests on the port used by the first mock server as that is what we used to get the base url. – Karan Dhillon May 09 '20 at 05:03

2 Answers2

11

Your second request is using the URL of the first MockWebServer instance. (Each instance has a distinct URL.)

Jesse Wilson
  • 39,078
  • 8
  • 121
  • 128
  • 2
    Interesting, but I don't see how that's the case. As I understand the `setUp` method happens before each test method, which means that a new instance of the mock server is created and a new instance of my server api is created along with the url of the newly created mock server. Or am I missing something here? – Nitzan Tomer Jun 22 '16 at 11:37
  • 2
    Ok, turns out that you're right. It shouldn't be the case in the example I posted as it's a simple one, but in my actual code I cache the address of the first mock server in my server api instance. thanks! – Nitzan Tomer Jun 22 '16 at 12:26
  • 1
    @JesseWilson @Jesse Wilson, I am stuck with the same issue. I also have two methods in the same class which tests API valid and invalid response, when I run it individually both test methods work fine. But when I run the whole class one of them works and the other gives Failed to connect to localhost/127.0.0.1:58688 error. Whereas if I check for port number `mockWebServer.getPort()` it provides two diffrent ports. How should I proceed with this issue? Thanks in advance. – Kavita Patil Oct 08 '19 at 19:13
-3

This localhost connection fails because it does not belong tot he mock server. Use this instead (mockHelper.getHost()).willReturn(okhttpMockWebServer.url("/").toString())

Tarun Koshta
  • 115
  • 1
  • 10