Swift unit testing with Realm and RAC - swift

I am trying to write some unit tests for my app. I am using Realm and RAC frameworks, but neither of them I can use in my unit tests.
import XCTest
#testable import FlightRecords
class RecordsViewModelTests: XCTestCase {
var viewModelUnderTest: RecordsViewModel!
override func setUp() {
super.setUp()
viewModelUnderTest = RecordsViewModel()
Realm.Configuration.defaultConfiguration.inMemoryIdentifier = self.name
}
}
In the code above, I get an error, but my app is working just normally with Realm (and RAC). The error is:
Use of unresolved identifier 'Realm'
I have tried different combinations for "Target Membership" and none of them worked. At this point, I have all frameworks' Target Membership for both app and tests.
Any advice?

You need to import RealmSwift in your test cases, also you should add the parent path to RealmSwift.framework to your unit test’s "Framework Search Paths".
See also https://realm.io/docs/swift/latest/#debugging

Related

Testing internal classes in swift framework

I did not find a way to unit test an internal swift class that is inside a framework. It only works if I set the class scope to public. Is there a way around it ?
Here is what I have now:
In the framework:
class InternalClass {}
In the tests:
import XCTest
#testable import MyFramework
class InternalClassTests: XCTestCase {
let sut = InternalClass() //ERROR HERE Use of unresolved identifier 'InternalClass'
}
If you want to use your InternalClass as internal class, you must add your InternalClass to your UnitTest target.
You can simple do it by click to the checkbox of the Unit Test target, in the file's Target Membership. (select your file and find it in the Inspectors bar)

Class implemented in app target and test target

I'm using CoreData and have extended the automatic (didn't generate custom class files) model classes by custom variables that return objects of classes coming from an CocoaPods dependency.
import Foundation
import ACocoaPodsFramework
import CoreData
extension MyClass {
var customVar: AClassFromCocoaPods? {
return AClassFromCocoaPods()
}
}
I've added a target for unit tests to the Xcode project and added MyClass and the xcdatamodeld to the test target. A test class looks like the following.
import XCTest
import ACocoaPodsFramework
import CoreData
#testable import MyAppTarget
class MyClassTests: XCTestCase {
}
I have imported ACocoaPodsFramework in both the app and the unit test target as a framework.
When i run the unit tests i receive the following notice
objc[21178]: Class MyClass is implemented in both /.../MyAppTarget.app/MyAppTarget (0x1017703f0) and /... MyAppTarget.app/PlugIns/MyClassTests.xctest/MyClassTests (0x123dc8ae8). One of the two will be used. Which one is undefined.
and exception
Could not cast value of type 'MyAppTarget.MyClass' (0x60c0000ccc60) to 'MyAppTargetTests.MyClass' (0x1258c9a20).
Solved this by removing all app files from the test target and instead importing the needed app files in the test classes using
#testable import MyAppTarget

removing runtime discovered tests labeled with "rt"

We are facing the issue that Xcode detects runtime tests.
Because of certain states of the tests these tests will always fail.
It's not possible to get rid of them, by cleaning the Project nor deleting the Derived Data.
Some Test classes are inherited by others.
Specs:
Xcode 8.3.3
Swift 3.1
Edit
Add more specific details
Subclass:
TestClassA: XCTestCase
func testA1()
func testA2()
TestClassB: TestClassA
func testB1()
Now the Problem is that when I run all tests for TestClassB Xcode will run testB1 and then testA1 and testA2. Both marked with rt and they will fail.
How can i achieve that this is not happening by not loosing the subclass structure.
Screenshot
I assume you're overriding the setup and teardown do assure the app will be in the proper state for every test case, the problem here would be that
the rT test will appear since the subclasses are inheriting the tests from their parent.
To avoid this while maintaining the subclassing structure, keep the setup and teardown functions in the classes you have but move the tests to a subclass of the setup class.
For example:
Subclass:
TestClassA: XCTestCase // This one will have the setup and teardown for A
TestClassATests: TestClassA
func testA1()
func testA2()
TestClassB: TestClassA // This one will have the setup and teardown for B, and you may reuse A setup
TestClassBTests: TestClassB
func testB1()
func testB2()

XCTest test failing, with no code in body

I am trying to create unit tests for my framework. I've created a 'Unit Testing Bundle' target and have my test class compiled in the target.
My test class extends XCTestCase.
I've left func testExample() empty, but still the test is failing. Why is this?

How do I import a Swift file from another Swift file?

I simply want to include my Swift class from another file, like its test
PrimeNumberModel.swift
import Foundation
class PrimeNumberModel { }
PrimeNumberModelTests.swift
import XCTest
import PrimeNumberModel // gives me "No such module 'PrimeNumberModel'"
class PrimeNumberModelTests: XCTestCase {
let testObject = PrimeNumberModel() // "Use of unresolved identifier 'PrimeNumberModel'"
}
Both swift files are in the same directory.
I had the same problem, also in my XCTestCase files, but not in the regular project files.
To get rid of the:
Use of unresolved identifier 'PrimeNumberModel'
I needed to import the base module in the test file. In my case, my target is called 'myproject' and I added import myproject and the class was recognised.
UPDATE Swift 2.x, 3.x, 4.x and 5.x
Now you don't need to add the public to the methods to test then.
On newer versions of Swift it's only necessary to add the #testable keyword.
PrimeNumberModelTests.swift
import XCTest
#testable import MyProject
class PrimeNumberModelTests: XCTestCase {
let testObject = PrimeNumberModel()
}
And your internal methods can keep Internal
PrimeNumberModel.swift
import Foundation
class PrimeNumberModel {
init() {
}
}
Note that private (and fileprivate) symbols are not available even with using #testable.
Swift 1.x
There are two relevant concepts from Swift here (As Xcode 6 beta 6).
You don't need to import Swift classes, but you need to import external modules (targets)
The Default Access Control level in Swift is Internal access
Considering that tests are on another target on PrimeNumberModelTests.swift you need to import the target that contains the class that you want to test, if your target is called MyProject will need to add import MyProject to the PrimeNumberModelTests:
PrimeNumberModelTests.swift
import XCTest
import MyProject
class PrimeNumberModelTests: XCTestCase {
let testObject = PrimeNumberModel()
}
But this is not enough to test your class PrimeNumberModel, since the default Access Control level is Internal Access, your class won't be visible to the test bundle, so you need to make it Public Access and all the methods that you want to test:
PrimeNumberModel.swift
import Foundation
public class PrimeNumberModel {
public init() {
}
}
In the Documentation it says there are no import statements in Swift.
Simply use:
let primNumber = PrimeNumberModel()
Check target-membership of PrimeNumberModel.swift in your testing target.
In Objective-C, if you wanted to use a class in another file you had to import it:
#import "SomeClass.h"
However, in Swift, you don't have to import at all. Simply use it as if it was already imported.
Example
// This is a file named SomeClass.swift
class SomeClass : NSObject {
}
// This is a different file, named OtherClass.swift
class OtherClass : NSObject {
let object = SomeClass()
}
As you can see, no import was needed. Hope this helps.
According To Apple you don't need an import for swift files in the Same Target. I finally got it working by adding my swift file to both my regular target and test target. Then I used the bridging header for test to make sure my ObjC files that I referenced in my regular bridging header were available. Ran like a charm now.
import XCTest
//Optionally you can import the whole Objc Module by doing #import ModuleName
class HHASettings_Tests: XCTestCase {
override func setUp() {
let x : SettingsTableViewController = SettingsTableViewController()
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testExample() {
// This is an example of a functional test case.
XCTAssert(true, "Pass")
}
func testPerformanceExample() {
// This is an example of a performance test case.
self.measureBlock() {
// Put the code you want to measure the time of here.
}
}
}
SO make sure PrimeNumberModel has a target of your test Target. Or High6 solution of importing your whole module will work
I was able to solve this problem by cleaning my build.
Top menu -> Product -> Clean
Or keyboard shortcut: Shift+Cmd+K
As of Swift 2.0, best practice is:
Add the line #testable import MyApp to the top of your tests file, where "MyApp" is the Product Module Name of your app target (viewable in your app target's build settings). That's it.
(Note that the product module name will be the same as your app target's name unless your app target's name contains spaces, which will be replaced with underscores. For example, if my app target was called "Fun Game" I'd write #testable import Fun_Game at the top of my tests.)
Check your PrimeNumberModelTests Target Settings.
If you can't see PrimeNumberModel.swift file in Build Phases/Compile Sources, add it.
You need to add a routine for the compiler to reference as an entry point, so add a main.swift file, which in this case simply creates an instance of your test file:
main.swift
PrimeNumberModelTests()
Then compile on the command line (I am using El Capitan and Swift 2.2):
xcrun -sdk macosx swiftc -emit-executable -o PrimeNumberMain PrimeNumberModel.swift PrimeNumberModelTests.swift main.swift
In this case, you will get a warning: result of initializer is unused, but the program compiles and is executable:
./PrimeNumberMain
CAVEAT: I removed the import XCTest and XCTestCase type for simplicity.
So, you need to
Import external modules you want to use
And make sure you have the right access modifiers on the class and methods you want to use.
In my case I had a swift file I wanted to unit test, and the unit test file was also a swift class. I made sure the access modifiers were correct, but the statement
import stMobile
(let's say that stMobile is our target name)
still did not work (I was still getting the 'No such module' error), I checked my target, and its name was indeed stMobile. So, I went to Build Settings, under packaging, and found the Product Module Name, and for some reason this was called St_Mobile, so I changed my import statement
import St_Mobile
(which is the Product Module Name), and everything worked.
So, to sum up:
Check your Product Module Name and use the import statement below in you unit test class
import myProductModuleName
Make sure your access modifiers are correct (class level and your methods).
Instead of requiring explicit imports, the Swift compiler implicitly searches for .swiftmodule files of dependency Swift libraries.
Xcode can build swift modules for you, or refer to the railsware blog for command line instructions for swiftc.
As #high6 and #erik-p-hansen pointed out in the answer given by #high6, this can be overcome by importing the target for the module where the PrimeNumberModel class is, which is probably the same name as your project in a simple project.
While looking at this, I came across the article Write your first Unit Test in Swift on swiftcast.tv by Clayton McIlrath. It discusses access modifiers, shows an example of the same problem you are having (but for a ViewController rather than a model file) and shows how to both import the target and solve the access modifier problem by including the destination file in the target, meaning you don't have to make the class you are trying to test public unless you actually want to do so.