4

My server code looks something like this:

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class Server implements Runnable {   
    private ServerSocket serverSocket;
    public Server(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }

    @Override
    public void run() {
        try {
            Socket client = serverSocket.accept();
            // do stuff
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

My plan was to write a mock client that connects to the server socket, and verifies the result, but in order to do that I need to know which port to connect to. However, this information is private.

None of the options I was thinking about are good practice I think:

  1. If I use a predefined port number for the tests, I have no guarantee that it will be available. Even if it's available just before the test, it might be, theoretically snatched by the time I try to use it.
  2. If I pass 0 as port number (so that ServerSocket will atomically provide a free port), I still have no access to it.
  3. I could add a getServerPort() method to the interface or create a constructor that accepts a ServerSocket object, but changing the interface only for the sake of testing is considered bad practice.
GhostCat
  • 137,827
  • 25
  • 176
  • 248
traveh
  • 2,700
  • 3
  • 27
  • 44
  • Usually you use JUnit to test the functional aspect of the code, not whether or not the connection will work. That is to say you separate `//do stuff` into its own function and write a test for that function so you don't have to work with passing a server port. – Susannah Potts Aug 15 '16 at 14:52
  • @SusannahPotts `// do stuff` is in a separate function, but that is private too... there is no public interface except a function that starts the server and a function that shuts it down. – traveh Aug 15 '16 at 14:54
  • [This question](http://stackoverflow.com/questions/34571/how-to-test-a-class-that-has-private-methods-fields-or-inner-classes) might help you with that. – Susannah Potts Aug 15 '16 at 14:56

1 Answers1

3

As written, your class is not really suited for unit test.

The problem is that your direct call to new ServerSocket() basically deprives your ability to control what the socket object will be doing.

So, what you can do:

interface SocketFactory {
  public ServerSocket createSocketFor(int port);
}

class SocketFactoryImpl implements SocketFactory {
...

public class Server implements Runnable {

  public Server(int port) {
    this(port, new SocketFactoryImpl());
  }

 Server(int port, SocketFactory socketFactory) {
   ...

In other words: you use dependency injection in order to provide a mean to your "class under test" to create those objects that it needs to do its work.

From there: you can use a mocking framework such as EasyMock in order to control what a mocked SocketFactory object will return (probably a mocked ServerSocket object). And now that you have full control over the ServerSocket used by your production code ... you can test whatever situation you want.

Long story short: don't call new; instead use dependency injection to gain full control over your class under test.

(and maybe watch these videos to really understand what writing testable code is about).

GhostCat
  • 137,827
  • 25
  • 176
  • 248