3

Background: I want to test private methods with XCTest, so I'm searching for a elegant solution to expose private methods to my XCTestCase classes.

Disclaimer: Some people have the opinion that it's fundamentally wrong to test the internals of your classes. In practice I disagree because when tests break, testing privates can make tracing down the issue much easier.

Swift private functions are private to the file, and private really means private, thus there is no way to expose private functions to other files. Using preprocessor macros, private functionality can be exposed via public functions for just one build configuration. For example, I have a TEST compile flag setup for my Test configuration which my Test scheme uses.

So far, this is the best solution I have, but it requires annoying boilerplate for every private function that needs testing:

In ClassThatNeedsTesting.swift:

class ClassThatNeedsTesting {
   #if TEST
   func publicAccessToPrivateFunctionThatNeedsTesting() -> Bool {
      return self.privateFunctionThatNeedsTesting()
   }
   #endif

   private func privateFunctionThatNeedsTesting() -> Bool {
      return true;
   }
}

In TestClass.swift:

class TestClass: XCTestCase {
   func testExample() {
      XCTAssert(ClassThatNeedsTesting().publicAccessToPrivateFunctionThatNeedsTesting());
   }
}

But is there a way to simplify this with inline preprocessor macros? This is what I want, but it doesn't compile, hence why I'm asking:

In ClassThatNeedsTesting.swift:

class ClassThatNeedsTesting {
   #if TEST private #endif func privateFunctionThatNeedsTesting() -> Bool {
      return true;
   }
}

In TestClass.swift:

class TestClass: XCTestCase {
   func testExample() {
      XCTAssert(ClassThatNeedsTesting().privateFunctionThatNeedsTesting());
   }
}

If there's no way to do this, perhaps there's a simpler solution?

Shizam
  • 9,627
  • 8
  • 51
  • 82
kev
  • 7,712
  • 11
  • 30
  • 41

1 Answers1

3

Use @testable, new in Swift 2.0:

@testable import MyModule

class TestClass: XCTestCase {
   func testExample() {
      XCTAssert(ClassThatNeedsTesting().privateFunctionThatNeedsTesting());
   }
}

From Xcode 7 Release Notes

Testability. With testability, you are now able to write tests of Swift 2.0 frameworks and apps without having to make all of your internal routines public. Use @testable import {ModuleName} in your test source code to make all public and internal routines usable by XCTest targets, but not by other framework and app targets.

Whether you should test private methods or not is a hot topic. See discussion here, here and here

Community
  • 1
  • 1
Code Different
  • 90,614
  • 16
  • 144
  • 163
  • 2
    This doesn't work. Apple's documentation you reference states that "Use `@testable` import {ModuleName} in your test source code to make all public and internal routines usable by XCTest targets." `@testable` only works for public and internal routines, not private routines. – kev Nov 10 '15 at 21:24
  • Just adding agreement with kev as I had the same issue. In my tests I got the following error when calling a private method: Use of unresolved identifier "PrivateMethodNameHere". Note if this were Obj-C you could call a private method and that's discussed here: http://stackoverflow.com/questions/12353795/objective-c-unit-testing-core-functionality-in-private-methods – xdeleon Nov 23 '15 at 02:07
  • How would you use @testable in objective-c? And, can you import your app as a module in objective-c? Please forgive the ignorance - good information is hard to find on this topic! – shmim Dec 22 '15 at 19:45