Tyler
Tyler

Reputation: 19858

Unit testing Swift 2.0, @testable import, and scheme targeting issues

I have recently converted my app, and unit tests over to Swift 2.0. I am using @testable import AppName in my Tests.swift files.

Due to an issue with not being able to execute subclasses in multiple targets (see here for the issue), my class EntityName can only be of Target Membership AppName, and NOT AppNameTests.

The problem is, once I switch over to the AppNameTests schema to run unit tests, code in the module AppName can't find the class EntityName and I get

Use of undeclared type 'EntityName'

How do I get AppName to compile when running tests from the AppNameTests scheme without that entity class not a member of the scheme?

Upvotes: 19

Views: 13876

Answers (5)

Pavle Mijatovic
Pavle Mijatovic

Reputation: 783

I had similar problem and the issue was that all of the application source files were linked to the unit test target, and so compiled twice!

Also, I installed Realm via Carthage and had to include both targets (main and test) for the frameworks "Realm.framework" and "RealmSwift.framework" because it wouldn't work otherwise.

Here is the issue link

https://github.com/realm/realm-cocoa/issues/3627

Upvotes: 0

Ben Fox
Ben Fox

Reputation: 1

First ensure that @testable import MyApp is included in every test file. Then, in your Test Target Build Phases, remove all non-test files the Copy Bundle Resources sections. App files that are in your test target that point to app files that are not in your test target are breaking your unit tests. Remove all app files from your test target and add the @testable flag and everything should work!

Upvotes: 0

amleszk
amleszk

Reputation: 6290

I also got this error recently and none of the above steps fixed the problem, what did fix it was removing non-swift file from the Compile sources build phase in the Target you want to run tests on. This was failing silently

enter image description here

Upvotes: 0

Tyler
Tyler

Reputation: 19858

I had to stop targeting my entire apps .swift files to be have membership of MyAppTests, and rely solely on @testable import MyApp

Upvotes: 10

jpsim
jpsim

Reputation: 14409

Due to an issue with not being able to execute subclasses in multiple targets

When compiling the same Swift code as part of different targets, the compiler actually generates different classes. So this behaves as designed, but is almost certainly not what you want when running an app's unit tests.

There are two ways I'd recommend you set up your models to allow testing:

1. Public models (recommended)

In your app target:

import RealmSwift
public class MyModel: Object {}

This code should only be compiled as part of your application target, and your unit tests can be set up in the following way:

import MyApp
// should be able to access `MyModel`

Easy enough?

2. @Testable (Swift 2 only)

This approach relies on the @testable keyword introduced in Swift 2.

In your app target:

import RealmSwift
internal class MyModel: Object {} // ACL cannot be `private`

Again, this code should only be compiled as part of your application target, and your unit tests can be set up in the following way:

@testable import MyApp
// should be able to access `MyModel`

Make sure MyApp's build settings have Enable Testability set to YES.

This approach may be preferred to public models if you're building a framework where some internal models shouldn't be accessible from users of that framework.

Realm has a dedicated section of their documentation detailing these common approaches to testing which you can read here: https://realm.io/docs/swift/latest/#avoid-linking-realm-and-tested-code-in-test-targets

Upvotes: 20

Related Questions