Let us suppose the side effect is ‘printing to a log’ or ‘sending a metric’. The following is a sample from drop wizard

private final static Logger log = Logger.getLogger(MyLogger.class.getName());
public void myMethod(){
    Jdbc.update(" <some sql>")
    log.info("Written to database")
    Meter meter = metricRegistry.meter("databaseMeter");
    meter.mark(1);
}

This is quite typical where we ‘do some database things’ (side effects) and in the same method log our results and notify the metrics system.

This code is really hard to test!

Let’s work out how we could simplify it. This is not about ‘whether its ‘right to use sql or prepared statements’, just about how we test what we have.

    final static String metricName = "databaseMeter"
    final static String sql = " <some sql>"

    private final  ILogger log; // configured by dependency injection
    private final IJdbcUpdate jdbc; // our interface that decouples us from the JDBC framework we are using
    private final IMetrics metrics; // our interface that decouples us from the metrics library we are using
    public void myMethod() {
        jdbc.update(sql);
        log.info("Written to database");
        metrics.mark(metricName, 1);
    }

Observe that now the code is really easy to test. And further we have decoupled ourselves from the libraries that we are using: a double bonus.

The test would now look like (assuming constructor injection)

    @Test
    public void testHappyPathWritesToDatabaseAndProducesLogsMetrics() {
        ILogger log = mock(ILogger.class);
        IJdbcUpdate jdbc = mock(IJdbcUpdate.class);
        IMetrics metrics = mock(IMetrics.class);
        JdbcLoggingMetrics myClass = new JdbcLoggingMetrics(log, jdbc, metrics);

        myClass.myMethod();

        verify(jdbc, times(1)).update(sql);
        verify(metrics, times(1)).mark(metricName, 1);
        verify(log, times(1)).info("Written to database");
    }

For extra points this could easily be split into three tests: one that checks the jdbc is called correctly, another that checks a log is called and another for the metrics.

After those tests we need to write some more

Previously all these tests had to be tested as part of the original code. Now we have decoupled them. If at some future date the metrics library or the logging library or the jbdc library are changed this test won’t be impacted. When we have other code using these interfaces they don’t need to test these methods.

Review

A few more tests are required for this method

It’s at this moment we realise that the code is actually complicated and that our simplistic approach was flawed

We can write the code to support this behavior in the code as it is, but it becomes complicated:

   public void myMethod() {
        try {
            jdbc.update(sql);
            log.info("Written to database");
            metrics.mark(successfulMetricName, 1);
        } catch (Exception e) {
            log.error("Error writing to database", e);
            metrics.mark(failedMetricName, 1);
        }
    }

And this doesn’t take into account what happens if the logging code throws an exception or the metrics code throws an exception. As we know anything in java can throw an exception (out of memory if nothing else)

How do we rewrite this?