Search code examples
unit-testingtestingclass-design

Is it ok to change the class interface to improve testability


I am currently in a discussion with a colleague who suggests that I separate out a component that is used in my class, create an abstract interface and set the concrete instance of that component from the outside. I could use this to create a mock in the unit test so that the testability of the class is improved.

Although I understand the argument very well, I feel that the design/interface of my class is negatively affected by this.

For me, this boils down to the question: is it okay to change the class interface for the sake of testability? What is more important, the interface or the testability?

Is it okay to ask the question in such a general way, or should I present an example?

Thank you very much.


Solution

  • If you are tempted to use a mock instead of the real class, then you are already designing for two interchangeable implementations: The real implementation and the test double. That's reason enough to extract out an interface.

    Testability is important; a pragmatic testable unit is much more useful than a "pure" untestable unit.

    Though you can label test-focused methods using names like setFooForTest or use test documentation annotations like @VisibleForTesting, ideally you should reflect that the test is a consumer of your interface and that your interface should be general enough for your consumer and your test. That might mean employing dependency injection or method overloads, like this:

    public int doThing() {
      return doThing(new DefaultCollaborator());
    }
    
    /** Does thing with a particular collaborator. Useful for testing. */
    /* package */ int doThing(Collaborator collaborator) {
      // ...
    }