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.Socket
s.
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.
Almost any socket program can run both halves on the same computer. Just run the server in one window, the client in another, and specify the connection address 127.0. 0.1 (which means localhost , your own computer). Show activity on this post.
Yes, Socket and ServerSocket use TCP/IP. The package overview for the java.net package is explicit about this, but it's easy to overlook. UDP is handled by the DatagramSocket class.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With