Swift - Unit testing private variables and methods

Solution 1:

You can't test private methods in Swift using @testable. You can only test methods marked either internal or public. As the docs say:

Note: @testable provides access only for “internal” functions; “private” declarations are not visible outside of their file even when using @testable.

Read more here

Solution 2:

Unit testing should be considered black box testing, which means you don't care about the internals of the unit you test. You are mainly interested to see what's the unit output based on the inputs you give it in the unit test.

Now, by outputs we can assert on several things:

  • the result of a method
  • the state of the object after acting on it,
  • the interaction with the dependencies the object has

In all cases, we are interested only about the public interface, since that's the one that communicates with the rest of the world.

Private stuff don't need to have unit tests simply because any private item is indirectly used by a public one. The trick is to write enough tests that exercise the public members so that the private ones are fully covered.

Also, one important thing to keep in mind is that unit testing should validate the unit specifications, and not its implementation. Validating implementation details adds a tight coupling between the unit testing code and the tested code, which has a big disadvantage: if the tested implementation detail changes, then it's likely that the unit test will need to be changed also.

Writing unit tests in a black box manner means that you'll be able to refactor all the code in those units without worrying that by also having to change the tests you risk into introducing bugs in the unit testing code. Unreliable unit tests are sometimes worse than a lack of tests, as tests that give false positives are likely to hide actual bugs in your code.