Testing Privates (or rather, internals)

With Xcode 6, Beta 4, we saw the introduction of access modifiers (maybe a better name is visibility modifiers). In essense, they control the visibility of the member throughout the module and outside of the module.

Here they are:

  • private specifies an item that is available only within the context of the file
  • internal specifies an item that is available only within the context of the module (e.g. the compiled target)
  • public specifies an item that is available only both within the module and to all modules that import the given module

My personal opinion here… I'm not sure. I like the simplicity, but I think I'd simply rather have private and public. Seems Brent Simmons agrees. Time will tell, as will further Xcode 6 seeds.

The question naturally arrises: how do I test private methods?

Well, we have three basic options:

  1. You don't and only test their usage through public APIs 2. You need to expose them publicly 3. You need to add the source to your test project so that they are part of the module and make then internal

Option #3 is the way that I'm currently doing it. Is it the best way? Probably not, but it does have the following advantages:

  • We can selectively choose while files to add test coverage for
  • There is no duplication of code signatures to maintain
  • It works under both debug and ship builds
  • There is no impact to the public API surface

Getting Started

To get started, simply create a new project that contains both a Swift target and a Swift XCTest target.

Next up, create a new Swift file. I created one called Person.swift.

I'll use this dummy class implementation:

public class Person {
    private var firstName: String
    private var lastName: String

    init(_ firstName: String, _ lastName: String) {
        self.firstName = Person.ensureValidName(firstName)
        self.lastName = Person.ensureValidName(lastName)
    }

    internal class func ensureValidName(name: String) -> String {
        if countElements(name) == 0 {
            return "<invalid name>"
        }
        else {
            return name
        }
    }
}

In this contrived example, we want to validate the function ensureValidName without actually invoking the initializer. Why might we want to do this? Well, simple: this function could be used in many different places in the API. For instance, we could put this in the setter implementation for firstName and lastName.

In order to test this method, we need to actually add Person.swift as a compile target to the XCTest target as well. Once you do that, the following test case will be valid:

import Cocoa
import XCTest
class TestingInternalsTests: XCTestCase {
    func testInvalidNameWithEmptyString() {
        XCTAssertEqual(Person.ensureValidName(""), "<invalid name>")
    }

    func testInvalidNameWithNonEmptyString() {
        XCTAssertEqual(Person.ensureValidName("David"), "David")
    }
}

There are a couple of drawbacks that we get with this approach:

  1. We really are breaking the semantic rules here with respect to private and internal; there is no good reason that internal should be used for this function, except to expose it to tests. 2. We are compiling the Person.swift into the XCTest target; this could get messy as your project gets larger and larger.

As always, use with caution and with care. Hacks are hacks for a reason.

Testing Privates (or rather, internals)