jeudi 31 mars 2016

How can a unit test "test the contract" on a method that returns void?

Java 8 here but this is a general unit testing question that (is likely) language-agnostic.

The syntax of writing a JUnit test is easy, but deciding on what tests to write and how to test main/production code is what I find to be the biggest challenge. In reading up on unit testing best practices, I keep hearing the same thing over and over again:

Test the contract

I believe the idea there is that unit tests should not be brittle and should not necessarily break if the method's implementation changes. That the method should define a contract of inputs -> results/outcomes and that the tests should aim to verify that contract is being honored. I think.

Let's say I have the following method:

public void doFizzOnBuzz(Buzz buzz, boolean isFoobaz) {
    // wsClient is a REST client for a microservice
    Widget widget = wsClient.getWidgetByBuzzId(buzz.getId());

    if(widget.needsFile()) {
        File file = readFileFromFileSystem(buzz.getFile());

        if(isFoobaz) {
            // Do something with the file (doesn't matter what)
        }
    }

    return;
}

private File readFileFromFileSystem(String filename) {
    // Private helper method; implementation doesn't matter here EXCEPT...
    // Any checked exceptions that Java might throw (as a result of working)
    // with the file system are wrapped in a RuntimeException (hence are now
    // unchecked.

    // Reads a file from the file system based on the filename/URI you specify
}

So here, we have a method we wish to write unit tests for (doFizzOnBuzz). This method:

  • Has two parameters, buzz and isFoobaz
  • Uses a class property wsClient to make a network/REST call
  • Calls a private helper method that not only works with the external file system, but that "swallows" checked exceptions; hence readFileFromFileSystem could throw RuntimeExceptions

What kinds of unit tests can we write for this that "test the contract"?

Validating inputs (buzz and isFoobaz) are obvious ones; the contract should define what valid values/states for each of those are, and what exceptions/results should occur if they are invalid.

But beyond that, I'm not really sure what the "contract" here would even be, which makes writing tests for it very difficult. So I guess this question really should be something like "How do I determine what the contract is for a unit test, and then how do you write tests that target the contract and not the implementation?"

But that title would be too long for a SO question.

Aucun commentaire:

Enregistrer un commentaire