One of the biggest challenges to Unit Testing in Swift was the initial setup. Before Swift 2, you either had to make everything public or remember to add all your files to the testing target (more on this in my blog post on testing in Swift here). But as of yesterday, this annoying testing issue has been elegantly solved!

All you need to do is:

1. Create Your Internal Class

I just created a super simple internal Model object in my brand new TestingTests project with one function:

Again, notice that I’m not adding this model to my test target and not making the class or my method public!

2. Import with @testable

In your test target, just import the module you want to test using the @testable keyword:

3. Test Away!

That’s it, now you can use all the internals of your class for testing purposes 🙂

Thank you Swift team!

Enjoy the article? Join over 20,000+ Swift developers and enthusiasts who get my weekly updates.

Behinder

Great to know. I still try to understand this whole testing stuff in obj-c

andreamazz

Hi Natasha, thanks for sharing
I’m so glad they added this, adding the public access control to stuff that should have been private was bothering me quite a bit, although it led to interesting discussion on what should be tested, and how.

Thanks for your sharing Natasha.
It would be better if you have a menu bar on your blog. I had alots of difficulties on finding older page.

raul_mpad

Thank you so much for the testable keyword solution to my problems.
I just had a problem with the code coverage’s new feature in Xcode 7 and now it’s gone after so many days trying to figure it out what was happening…

jeremychone

I have xCode 7 beta2, and somehow, even when @testable …, I still need to set my .swift target to the test target? I am on a command line Mac OSX project.

Nicole Lehrer

Thanks Natasha for your blog and the helpful info. I am having the same issue as @jeremychone:disqus for an IOS project.

Nicole Lehrer

This was my bad – I was using
@testable import swiftFileName
instead of
@testable import moduleName
and it works just as Natasha describes.

jeremychone

Hum, I had the moduleName one, but still had add my files to test target. I will give it another try.

Zacharias Beckman

Same problems here. Seems like it’s not quite ready for prime time. I tried creating a brand new testing target, adding one new test case, made sure “Enable Testability” is YES, but no go. Definitely not working… perhaps in beta 6… (crossing my fingers! This would be a fantastic feature!)

Victor Ji

Same problem with Xcode 7 beta 2.

Mark Anders

Hi @jeremychone:disqus , in the project settings/Build Settings in the Packaging section for your app, did you set “Defines Module” to Yes?

Thanks for writing this little tutorial up, Natasha. IMO this would be a little more useful if the project wasn’t named TestingTests, kind of gets confusing with the actual testing code you’re implementing

I am using Xcode 7 beta4, and on adding the @testable keyword before importing the module am getting the ‘No such module ‘ . Though after the @testable keyword, am able to access all the internal swift classes, but it throws this error while compiling for testing. My product module name is set to target name and I have checked this part for main product target. How should I solve this problem ?

This method will not work if your project is using both Objective-C and Swift. This method is for only total swift based project. That’s what I found in my project and reading few other blogs

Dominic Frei

Even though you are talking about a beta and Xcode 7 is now stable, I had that problem within the release version of Xcode 7, too.
In case someone stumbles upon this post in the future: A clean did help!

Christopher Sanders

Thank you for this! I really needed this!!!

Tyler Williamson

Unfortunately, my classes that are not members of my tests schema are not visible from within their own module. Use of undeclared type ‘ClassName’ from within its own module! Ugh!

You shouldn’t test private methods. But you can test their side-effects in the internal / public methods that use them.

Dheeraj

Okay, thanks for replying. Can you suggest few ways to test the code effectively?

Steve

Google is your friend.

Zacharias Beckman

I agree – testing should generally focus on public APIs/methods… BUT, technically, with @testable you can now test private methods (requires Xcode 7, Swift 2, and some creativity). There have been a few cases where I’ve really, really wanted to know what would happen if, say, a private internal method returned something that it would not normally return. Subclass it, override the method, inject the mock object, and there you go – use @testable if needed to get easier access.

Chad Nelson

Why shouldn’t private methods be tested?

Steve

Because testing private makes refactoring a problem. Treat your objects like black boxes when testing and then you will be on the right track.

DamienSan

I don’t see in what it makes refactoring a problem. On the opposite, the unit testing will ensure your refactoring will be done correctly.

Steve

Well let us know how that works out for ya then!

DamienSan

Well on young & lagging behing languages like Swift: it’s a mess.

On mature language like Erlang: it’s a bliss. There are 2 differents frameworks included, EUnit for white box testing, Common Test for black box testing.

Each are optimized for a specific use case and covers everything.

DamienSan

You should test private methods too: it’s called white-box testing (as opposed to black-box testing). A lot of internal processing can happen in private scope and you need to ensure that each step is going right.

The scope of the function shouldn’t be used as a decision for not testing it.

* iOS application with a mix of ObjC and Swift classes.
* Unit test class written in ObjC.

Is it possible for the ObjC unit test class to access the public Swift classes in the main target (I don’t think assigning them to both the main target and the ObjC test target would work)? I’m able to import normal ObjC classes that aren’t assigned to the test target into the unit test class and I’m able to create a Swift unit test class and import ObjC classes that similarly don’t have test target membership into it, but not for the ObjC test target importing Swift classes in the main target.

The main target does have module defines module set to YES and its module name is set, which is how I’m able to import the ObjC classes into the Swift based unit test class.

#import “MainTargetModuleName-Swift.h” won’t work since this auto generated header is the value for the SWIFT_OBJC_INTERFACE_HEADER_NAME build setting on the main target, and the test target doesn’t know about it.

TestingTests must be the name of the Product Module Name that can be different from the folders name as image show above. That can lead you to search why don’t recognise the ‘TestingTest’. Please add a warning about that.. to save time :)X Very good post:)X Thanks

Nicolás Miari

I still don’t understand why we have to perform this (admittedly, east) step for the unit tests target, but the UI tests target works right out of the box.

goblin

Nice!
It worked for iOS than I tried with Command Line Application but it didn’t work out. Some linker problems

I get the error:
‘Use of instance member ‘queryHistoricValues’ on type ‘ViewController’; did you mean to use a value of type ‘ViewController’ instead?’

Its expecting ViewController as a parameter to that function?

Ace Green

Just an additional note, when I click on BitLive (import) I only see the public classes of my app. Not sure if this is the expected behavior or something is fishy. The “ViewController” class is recognized to it though