Test public API, not implementation details

There are good reasons why programmers perform tests themselves in contrast to letting an external testing department (QA) perform the tests. The programmer is able to discover errors in code and in general design decisions much faster and can react immediately, as one might say, the feedback loop is very short. On the other hand, when programmers have both, the test and the implementation in mind, they’re tempted to test code that an external wouldn’t test, simply because the external only know about the spec but not the implementation. That way I once wrote way more tests than needed and at the time of writing the tests felt confident with that approach because more tests are better than less tests and JavaScripts lacking of private members literally encouraged me to do this anyway. Well, I paid the price some weeks later, when a minor change took me way to long to implement because of some dozens of failing tests that shouldn’t be affected by my change. But what exactly is bad about testing other things than public API?

Consider the following little code snippet:

What is public API?

First of all, make yourself aware of what methods of your Auth class are meant to be accessible and visible to the user of the class. That’s the public API. In this case the only method that is supposed to be visible is:

boolean function login(username:String, password:String)

What are implementation details?

Now that you have settled the public API, everything else can be considered as implementation detail, including the _isEmpty method. I use a single underscore prefix to make the distinction more visible. But that’s only a coding convention and not a JavaScript feature and therefore doesn’t prevent a tester from using it per se. It can help code-reviewers with detecting private members though.

Tip: A user of your class Auth only cares about public API because that’s the only API that is well documented and maintained. So let’s make sure that this API works as expected and only test login. Applying this approach keeps the amount of tests as small as possible but effective as necessary.

A bad test

Testing Terminology: A test that fails, although the code changes shouldn’t have affected the test are often called: Fragile Test

Good tests

Focus on simplicity, correctness and shortness of tests. You and your co-workers will enjoy the readability. Internal documentation meant for developers can be replaced by your tests. From now on your tests are the spec. What about refactoring? Super easy. Let’s say you replace _isEmpty for internal reasons by underscrore.isEmpty() and some days later by lodash.isEmpty(). No problem, you don’t even need to touch the tests. Just execute them and give yourself the confidence that login still works as expected. This process doesn’t only make refactoring easy, it encourages you to make your code continually better and let you react fast on internal design decisions. Code-reviews of refactoring steps can happen faster because one thing is sure, whatever your code does, it doesn’t break the public API because you didn’t touch the tests.

Summary

KISS! Testing less but the right things improves your development process and makes you and your co-workers happy. Implementation details are never mentioned within the spec, so they never appear within your tests.

Testing implementation details

  • makes refactoring hard
  • increases the cost of test maintenance
  • does not improve the API
  • slows down development process
  • a waste of time

Testing public API

  • improves readability of tests
  • your tests are the specification
  • less but better tests
  • refactor fast without touching tests
  • faster code review