Testability: How much logic belongs in the constructor?

The other day my colleague and I were pair-programming and we came across a dilemma regarding the design of a class which was responsible to load the configuration properties of our application. The class looked like the following (a little oversimplified):


class ApplicationConfig {

private Connection connection;

public ApplicationConfig(String connectionUrl, String driver) {
 try {
 Class.forName(driver);
 this.connection = DriverManager.getConnection(connectionUrl);
 } catch (ClassNotFoundException e) {
 throw new IllegalArgumentException("Problems while loading JDBC Driver.", e);
 } catch (SQLException e) {
 throw new IllegalArgumentException("Problems while creating JDBC Connection.", e);
 }
 }

public Connection getConnection() {
 return connection;
 }
 }

In order to achieve better testability by minimizing side-effects, it´s highly recommended to strive for simple constructors that do nothing other than making simple assignments to fields. As Misko Hevery comments on his Guide for Writing Testable Code, constructors should do no real work. Mark Needham, in turn, advocates the simplification of constructors by delaying calculations. He points out the idea of reducing field calculation in the constructor, only calculating those values when they´re actually needed, which generally is a while after the object has been constructed.

Following this advice, we moved the creation of the connection to the getConnection() method, which led us to the following:


class ApplicationConfig {

private String connectionUrl;

private String driver;

public ApplicationConfig(String connectionUrl, String driver) {
 this.connectionUrl = connectionUrl;
 this.driver = driver;
 }

public Connection getConnection() {
 try {
 Class.forName(driver);
 return DriverManager.getConnection(connectionUrl);
 } catch (ClassNotFoundException e) {
 throw new ConfigException("Problems while loading JDBC Driver.", e);
 } catch (SQLException e) {
 throw new ConfigException("Problems while creating JDBC Connection.", e);
 }
 }
 }

OK, now we have a simple constructor. However, the problem with this design is that we will only be able to check if the connection arguments are truly valid in the getConnection() method, which is too later after the object is constructed (you can also notice the different exceptions that are used). The previous design allowed us to signal the problem as soon as possible (fail-fast), which helps to improve bug tracking.

So the issue is whether we should favor the fail-fast rule or the testability. We opted for the latter since this configuration code is pretty much under our control, that is, the risk of passing invalid arguments to the constructor is low.

Just as an aside: we could eliminate the problem altogether by injecting the connection into the ApplicationConfig class, but the point of having the ApplicationConfig class in the first place was that it could be easily reused in other places, removing the duplication of the boilerplate code. The purpose of the class is to be a utility class to load configurations, so we found it reasonable for it to have the responsibility to instantiate a connection.

I´d appreciate comments and suggestions on how to address this issue.

5 thoughts on “Testability: How much logic belongs in the constructor?

  1. Greetings, Alexandre!

    The main testability problem I usually see in complex constructors is not exactly “real work”, but rather the dependency on classes that are unknown/invisible from outside. This makes the constructor difficult to isolate and test.

    In the example, I see the dependency to the class DriverManager as a testability problem either in the constructor or in the getConnection method. So I suggest DriverManager should be injected.

    I would also suggest delegating the specific treatment in the connection creation in order to simplify and clarify your design’s responsibility distribution, which would also improve its testability.

    Cheers,
    Rafael

  2. Hello Rafael,

    thank you a lot for your comments!

    Yes, the main problem is actually related to depedency. As I pointed out at the end, the problem could be completely eliminated by injecting the Connection. But then, the caller would have to deal with these issues itself. Since the responsibility of this class is to provide such functionality, we found it appropriate for it to deal with the creation of the connection. Maybe, a better approach could be to use a DI container or the like. But for our application I think that would be overkill.

    Anyhow, I think Misko Hevery’s advice regarding logic in the constructor is a good guideline to strive for. Besides the dependency issue, we should watch out for the side effects unleashed by the constructor, which might affect the test results.

    All the best,

    Gazola

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s