34

I'm developing a network application and I want to get unit testing right. THIS time we'll do it, you know? :)

I'm have trouble testing network connections, though.

In my application I use plain java.net.Sockets.

For example:

import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

public class Message {
    byte[] payload;

    public Message(byte[] payload) {
        this.payload = payload;
    }

    public boolean sendTo(String hostname, int port) {
        boolean sent = false;

        try {
            Socket socket = new Socket(hostname, port);

            OutputStream out = socket.getOutputStream();

            out.write(payload);

            socket.close();

            sent = true;
        } catch (UnknownHostException e) {
        } catch (IOException e) {
        }

        return sent;
    }
}

I read about mocking but am not sure how to apply it.

Rob Kielty
  • 7,958
  • 8
  • 39
  • 51
Zoran Zaric
  • 1,211
  • 2
  • 11
  • 19

4 Answers4

61

If I was to test the code, I'd do the following.

Firstly, refactor the code so that the Socket isn't directly instantiated in the method you want to test. The example below shows the smallest change I can think of to make that happen. Future changes might factor out the Socket creation to a completely separate class, but I like small steps and I don't like making big changes on untested code.

public boolean sendTo(String hostname, int port) {
    boolean sent = false;

    try {
        Socket socket = createSocket();
        OutputStream out = socket.getOutputStream();
        out.write(payload);
        socket.close();
        sent = true;
    } catch (UnknownHostException e) {
        // TODO
    } catch (IOException e) {
        // TODO
    }

    return sent;
}

protected Socket createSocket() {
    return new Socket();
}

Now that the socket creation logic is outside of the method you are trying to test, you can start to mock things up and hook into the creation the socket.

public class MessageTest {
    @Test
    public void testSimplePayload() () {
        byte[] emptyPayload = new byte[1001];

        // Using Mockito
        final Socket socket = mock(Socket.class);
        final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        when(socket.getOutputStream()).thenReturn(byteArrayOutputStream);

        Message text = new Message(emptyPayload) {
            @Override
            protected Socket createSocket() {
                return socket;
            }
        };

        Assert.assertTrue("Message sent successfully", text.sendTo("localhost", "1234"));
        Assert.assertEquals("whatever you wanted to send".getBytes(), byteArrayOutputStream.toByteArray());
    }
}

Overriding individual methods on units you want to test is really useful for testing, especially in ugly code with horrible dependencies. Obviously the best solution is sorting out dependencies (in this case I would think that a Message not depend on a Socket, maybe there is a Messager interface as glowcoder suggests), but it's nice to move towards the solution in the smallest possible steps.

Steve Chambers
  • 37,270
  • 24
  • 156
  • 208
Jeff Foster
  • 43,770
  • 11
  • 86
  • 103
  • I'm having a `java.lang.VerifyError` exception when executing `mock(Socket.class)`, do you happen to know why? – Jo Colina Apr 12 '16 at 10:00
  • You can also "assert" that the message was sent by `verify`-ing that `write` method was called, for example: `verify(byteArrayOutputStream).write(any(byte[].class))` – Eido95 Aug 26 '18 at 15:23
11

I'm going to answer your question as asked instead of redesigning your class (others have that covered, but the basic question on the class as written is stil valid).

Unit testing never tests anything outside the class being tested. This hurt my brain for a while--it means unit test does not in any way prove that your code works! What it does is prove that your code works the same way it did when you wrote the test.

So that said you want a unit test for this class but you also want a functional test.

For the unit test you have to be able to "Mock out" the communications. To do this instead of creating your own socket, fetch one from a "Socket factory", then make yourself a socket factory. The factory should be passed in to the constructor of this class you are testing. This is actually not a bad design strategy--you can set the hostname and port in the factory so you don't have to know about them in your communication class--more abstract.

Now in testing you just pass in a mock factory that creates mock sockets and everything is roses.

Don't forget the functional test though! Set up a "test server" that you can connect to, send some messages to the server and test the responses you get back.

For that matter, you probably want to do even deeper functional tests where you write a client that sends the REAL server some scripted commands and tests the results. You probably even want to create a "Reset state" command just for functional testing. Functional tests actually ensure that entire "Functional units" work together as you expect--something that many unit testing advocates forget.

Bill K
  • 62,186
  • 18
  • 105
  • 157
  • _...send some messages to the server and **test** the responses you get back. ..._ how would you **test** it? manually? is there an approach to do it automatically? – Eido95 Aug 26 '18 at 15:10
  • Well, if you are testing a live server it should actually do something. Whatever it should do, you can test. If it has a user/password, test passing and failing user/password combinations. If it just returns the string "I'm here", assert that you get that string when you connect. Whatever it responds, you should be able to test. We have cases where we execute a query of activity in the last 24 hours and expect to get >1 record, that's a test that has saved us a few times. – Bill K Aug 27 '18 at 15:48
1

I'm not going to say this is a bad idea.

I am going to say it can be improved upon.

If you're sending across a raw byte[] over your socket, the other side can do whatever it wants with it. Now, if you're not connecting to a Java server, then you might NEED to do that. If you're willing to say "I'm always working with a Java server" then you can use serialization to your advantage.

When you do this, you can mock it by just creating your own Sendable objects as if they came across the wire.

Create one socket, not one for every message.

interface Sendable {

    void callback(Engine engine);

}

So how does this work in practice?

Messenger class:

/**
 * Class is not thread-safe. Synchronization left as exercise to the reader
 */
class Messenger { // on the client side

    Socket socket;
    ObjectOutputStream out;

    Messenger(String host, String port) {
        socket = new Socket(host,port);
        out = new ObjectOutputStream(socket.getOutputStream());
    }

    void sendMessage(Sendable message) {
        out.writeObject(message);
    }

}

Receiver class:

class Receiver extends Thread { // on the server side

    Socket socket;
    ObjectInputStream in;
    Engine engine; // whatever does your logical data

    Receiver(Socket socket, Engine engine) { // presumable from new Receiver(serverSocket.accept());
        this.socket = socket;
        this.in = new ObjectInputStream(socket.getInputStream());
    }

    @Override
    public void run() {
        while(true) {
            // we know only Sendables ever come across the wire
            Sendable message = in.readObject();
            message.callback(engine); // message class has behavior for the engine
        }
    }
}
corsiKa
  • 81,495
  • 25
  • 153
  • 204
  • thanks youfor your answer, but i will in fact interact with non java servers and clients, so serialization isn't an option for me. – Zoran Zaric Apr 13 '11 at 12:00
  • 2
    While you are likely trying to test existing code, someone else like me may be doing research for a new project. For that guy -- there is platform independent serialization. Check out https://github.com/google/protobuf/ – kd8azz Dec 24 '14 at 03:54
  • 1
    Or simple JSON for that matter. – Piovezan Jul 28 '17 at 18:41
  • Kind of get what you're doing, altough all interfaces aren't present. A working answer would make things much clearer! What about receiving? – KRK Owner Jul 19 '20 at 22:55
  • @user924272 Examples don't usually include the `import` statements. Socket is from the `java.net` package. ObjectInputStream is from the `java.io` package. Engine is fake and made up for this example. What about the receiving example is unclear? – corsiKa Jul 19 '20 at 23:36
-2

It's difficult testing connection and server interactions.

Tipically, I isolate business logic from communication logic.

I create scenarios for unit tests on business logic, these tests are automatic (use JUni,t and Maven) and i create other scenarios to test real connections, I don't use framework like JUnit for these tests.

In last year I've used Spring Mocks to test logic with HttpResponse HttpRequest, but I think this is not useful.

I follow this question.

Bye

m.genova
  • 377
  • 6
  • 15
  • 2
    I also try to seperate business- and communication logic, but also want to test my communication code. I've found Jeff Foster's solution very helpful. – Zoran Zaric Apr 13 '11 at 11:52