Tuesday, October 28, 2014

Applying Interfaces to external dependencies: Golang

  Many object oriented languages center on the interface as a communication contract between components.  Engineering classes with this in mind typically enhances the maintainability and reusability of code by allowing dependencies to be injected, agnostic to the particular implementation.  Good unit tests take advantage of this by mocking all dependencies at the interface level, and injecting them into the unit under test.  This becomes very important at the boundaries of a system, where complex interactions with other external systems can make testing a nightmare.

  As good a practice as this is, many libraries that we find ourselves depending on don't implement an interface.  Consequently, we find ourselves having to make the decision on if we want to include those dependencies within the scope of our tests, or if we want to extract an interface from the library, then create an adapter wrapper linking the original library to the new interface, and finally a separate mock, also implementing the interface.  All this work to mock out the external dependency, and you would still have holes in your coverage in the adapter, unless you wanted to then drag the external dependency into your tests again.  Yuk!

  Enter Golang duck typing.  More commonly seen in dynamic languages, the duck typing mantra asserts that "if it quacks like a duck, it is a duck".  The behavior implies the type, rather than explicitly having to call it out through inheritance.  Any class that implements Quack() is implicitly a duck, even if the original developer never made that distinction.

  So how does that impact our testing strategy?  We simply create an interface with signatures that  match the pieces of our dependency we actually use.  Our tests can use mocks conforming to this interface, and we don't have the overhead of an adapter, nor do we have the holes in our test coverage or the complexity of scoping tests to include the dependency.  Its a pretty nice use case for applying interfaces after the fact.