Unit Test Private Methods

Unit testing a Swift project is quite different from unit testing a project written in Objective-C. If you’re used to the flexibility of the Objective-C runtime, then it may feel as if your hands are tied behind your back. Right?

Access Control

While access control is a very welcome addition with many benefits, it can complicate unit testing, especially if you’re new to unit testing. You probably know that you can apply the testable attribute to an import statement in a test target to gain access to entities that are declared internal.

Image

While this is a convenient addition, it doesn’t give you access to private entities in a test target. This brings us to the question of the day. How do you unit test private entities?

Wrong Question

The short answer to this question is simple. You cannot access private entities from another module and this also applies to test targets. Plain and simple. That’s what access control is for.

But that isn’t the answer to the question. If you ask how to unit test private entities, then you’re asking the wrong question. But why is that?

Mind Your Own Business

Why do you declare an entity private? What’s your motivation for doing so? Take a look at the following example.

Image

I’d like to unit test the AccountViewViewModel structure. As you can see, the AccountViewViewModel struct exposes two internal computed properties and it also defines a private method. The expiresAtAsString computed property offloads some of its work to the private parse(date:) method. Testing the internal computed properties is straightforward.

Image

But how do we test the private method? We cannot access the private method from the test target. But why should we unit test the private method? We marked it as private for a reason. Right? And that brings us to the answer to the question we started with. We don’t test private methods.

Unit Testing the Public Interface

By unit testing the public interface of the AccountViewViewModel struct we automatically or implicitly unit test the private interface of the struct. You have the task to make sure the public interface is thoroughly tested. This means that you need to make sure every code path of the AccountViewViewModel struct is covered by unit tests. In other words, the suite of unit tests should result in complete code coverage. That includes public, internal, and private entities.

If we enable code coverage in Xcode and we run the unit tests of the AccountViewViewModelstruct, we can see that some code paths are not executed.

Image

This tells us that the unit tests are incomplete. We can ignore the code path for the fatal error. I never unit test code paths that result in a fatal error, but that largely depends on how you use fatal errors in your projects.

We can increase code coverage for the AccountViewViewModel struct by adding one more unit test.

Image
Image

Implementation and Specification

It’s important to understand that we’re testing the specification of the AccountViewViewModelstruct. We’re not testing its implementation. While this may sound similar, it’s actually very different. We’re testing the functionality of the AccountViewViewModel struct. We’re not interested in how it does its magic under the hood.

The key takeaway is that private entities don’t need to be unit tested. Unit testing is a form of black-box testing. This means that we don’t test the implementation of the AccountViewViewModel struct, we test its specification.

This doesn’t mean that we’re not interested in the implementation, though. We need to make sure the suite of unit tests covers every code path of the entity we’re testing. Code coverage reports are invaluable to accomplish this.

Advertisements
Unit Test Private Methods

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s