14

I need to test the following method with out altering the method itself. The method makes a POST method to a server. But I need to make a test case that's independent from the server.

I tested a similar method before redirecting it to a local file. But that for that I was giving protocol as file, hostname as localhost and port as -1.

My problem is that this method does a post and casts to HttpURLConnection and wr = new DataOutputStream(conn.getOutputStream()); wont work on an local txt file through http.

//the constructor

public HTTPConnector(String usr, String pwd, String protocol,
            String hostname, int port) {
        this.usr = usr;
        this.pwd = pwd;
        this.protocol = protocol;
        this.hostname = hostname;
        this.port = port;

        // connect();
    }

//the method I need to test

public String doPost(String reference, String data) throws IOException {
        URL url = null;
        HttpURLConnection conn = null;
        BufferedReader rd = null;
        DataOutputStream wr = null;
        InputStream is = null;
        String line = null;
        StringBuffer response = null;

        url = new URL(protocol, hostname, port, reference);
        conn = (HttpURLConnection) url.openConnection();

        conn.setRequestMethod("POST");

        conn.setRequestProperty("Authorization", "Basic dGVzdDphc2Rm");
        conn.setRequestProperty("Content-Type", "application/xml");

        conn.setUseCaches(false);
        conn.setDoInput(true);
        conn.setDoOutput(true);

        // Send response
        wr = new DataOutputStream(conn.getOutputStream());
        wr.writeBytes(data);
        wr.flush();
        wr.close();

        // Get response
        is = conn.getInputStream();
        rd = new BufferedReader(new InputStreamReader(is));
        response = new StringBuffer();
        while ((line = rd.readLine()) != null) {
            response.append(line);
            response.append('\r');
        }
        rd.close();
        return response.toString();
    }

//the method I could test

public String doGet(String reference) throws IOException {
        connect();
        URL url = new URL(protocol, hostname, port, reference);
        InputStream content = (InputStream) url.getContent();

        BufferedReader xml = new BufferedReader(new InputStreamReader(content));
        return xml.readLine();
    }
2
  • have you looked at using mock objects? Commented May 17, 2012 at 12:05
  • wel it doesnt goes to anything local so i find it hard how to redirect it. if i can find a way to redirect it to a local file or class then i could make a work around, but the only way i can see is to make a local server/thread. but that means i got to direct my tests to the server, to get data there and the server it self needs to be programed to handel to request and thats much more work then efficient. Commented May 17, 2012 at 12:11

2 Answers 2

7

Here's a sample test. Note that assertions I made are for demo purposes, you need to adapt to your needs.

@RunWith(PowerMockRunner.class)
@PrepareForTest({ toTest.class, URL.class, HttpURLConnection.class })
public class soTest {
    /**
     * test response.
     */
    private static final String TEST_RESPONSE = "test\nresponse";

    /**
     * test data.
     */
    private static final String DATA = RandomStringUtils.randomAscii(125);

    /**
     * test port.
     */
    private static final int PORT = 8080;

    /**
     * test hosts.
     */
    private static final String HOSTNAME = "hostname";

    /**
     * test protocol.
     */
    private static final String PROTOCOL = "http";

    /**
     * test reference.
     */
    private static final String REFERENCE = "REFERENCE";

    /**
     * URL mock.
     */
    private URL url;

    /**
     * HttpURLConnection mock.
     */
    private HttpURLConnection connection;

    /**
     * Our output.
     */
    private ByteArrayOutputStream output;

    /**
     * Our input.
     */
    private ByteArrayInputStream input;

    /**
     * Instance under tests.
     */
    private toTest instance;

    @Before
    public void setUp() throws Exception
    {
        this.url = PowerMockito.mock(URL.class);
        this.connection = PowerMockito.mock(HttpURLConnection.class);

        this.output = new ByteArrayOutputStream();
        this.input = new ByteArrayInputStream(TEST_RESPONSE.getBytes());
        this.instance = new toTest(PROTOCOL, HOSTNAME, PORT);

        PowerMockito.whenNew(URL.class).withArguments(PROTOCOL, HOSTNAME, PORT, REFERENCE).thenReturn(this.url);
    }

    @Test
    public void testDoPost() throws Exception
    {
        PowerMockito.doReturn(this.connection).when(this.url).openConnection();
        PowerMockito.doReturn(this.output).when(this.connection).getOutputStream();
        PowerMockito.doReturn(this.input).when(this.connection).getInputStream();

        final String response = this.instance.doPost(REFERENCE, DATA);

        PowerMockito.verifyNew(URL.class);
        new URL(PROTOCOL, HOSTNAME, PORT, REFERENCE);

        // Mockito.verify(this.url).openConnection(); // cannot be verified (mockito limitation) 
        Mockito.verify(this.connection).getOutputStream();
        Mockito.verify(this.connection).setRequestMethod("POST");
        Mockito.verify(this.connection).setRequestProperty("Authorization", "Basic dGVzdDphc2Rm");
        Mockito.verify(this.connection).setRequestProperty("Content-Type", "application/xml");
        Mockito.verify(this.connection).setUseCaches(false);
        Mockito.verify(this.connection).setDoInput(true);
        Mockito.verify(this.connection).setDoOutput(true);
        Mockito.verify(this.connection).getInputStream();

        assertArrayEquals(DATA.getBytes(), this.output.toByteArray());
        assertEquals(TEST_RESPONSE.replaceAll("\n", "\r") + "\r", response);
    }
}


@Data
public class toTest {
    private final String protocol, hostname;

    private final  int port;

    public String doPost(String reference, String data) throws IOException
    {
        // your method, not modified
    }
}

dependencies:

  • commons-lang 2.5
  • powermock-api-mockito 1.4.11
  • powermock-module-junit4 1.4.11
  • junit 4.10
  • lombok 0.11.0 on tested class
Sign up to request clarification or add additional context in comments.

2 Comments

it seems like a good sollution, but i get an error and dont know how to solve it maybe you can shed some light: The type org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner cannot be resolved. It is indirectly referenced from required .class files
This class is provided by powermock-module-junit4, that's strange. You should check your classpath
6

If you don't have the opportunity to refactor the method under test, then a novel approach would be to mock the HttpUrlConnection that it uses. At first hand this would appear to be difficult because the HttpUrlConnection is not passed in as a parameter. However, you can control this by controlling the connection returned from url.openConnection.

This is governed by the protocol handler registered with java.net.URL for the protocol that you pass to the constructor. The trick is to register a new protocol handler (see Java - Registering custom URL protocol handlers for details).

Your new protocol handler should return a mocked HttpUrlConnection that you can then use in your test.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.