简体   繁体   中英

Dependency injection for unit testing vs. class managing its own variables

I have a class that is something like this (very simplified but this is the general idea. I'm not actually using the raw socket type):

class ConnectionBroker {

    private Socket socket;

    public ConnectionBroker(ConnectionDetails details) {
        socket = new Socket(details)
        //set socket paramaters
    }

    public sendOverConnection(String message) {
        //processing of message and internal queuing based on priority
        socket.send(message);
    }

    public shutdown() {
        socket.close();
    }
}

It would be valuable if I could unit test the class's functions by mocking the socket variable to check what is being sent over the socket. The standard way of doing this that I have seen is modifying the constructor to be

public ConnectionBroker(Socket socket) {
    this.socket = socket;
}

This would make unit testing a lot easier, however I have a number of issues with doing it this way:

  • The socket should be maanged only by the ConnectionBroker class
  • The fact that the ConnectionBroker class uses sockets at all should be abstracted away from the calling class
  • If the socket needs to be passed in then the calling code could possibly modify attributes on the socket which the the ConnectionBroker class is not aware of

What is the proper way of solving this? Am I thinking of something incorrectly?

Not sure if ConnectionBroker(Socket socket) is the standard way, it has its pros and cons as you pointed out.

Obviously the problematic bit of your code is the call to new Socket(details) , which hinders testing.

In an almost similar scenario I introduced a new dependency SocketProvider with a single method getSocket() that ends up creating the socket:

public Socket getSocket() { return new Socket();

This makes it trivial for unit tests to provide an implementation or subclass that returns a mock / stub / spy... Eg

@Mock 
private Socket mockedSocket;
@Mock 
private SocketProvider mockedProvider;

// ...
conncectionBroker.setSocketProvider(mockedProvider);

// ...

when(mockedProvider.getSocket()).return(mockedSocket); // or a stub / spy / testSocket...

It also allows to provide different implementations for different environments and testing scenarios - which IMO makes it the nicer approach to the one that you suggested (where the creation logic lays within the responsibility of an individual test).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM