XCTestCase optional instance variable - swift

Why is my optional instance variable nil when I infact set it to non-nil?
Code:
class FooTests: XCTestCase {
var foo: Int?
func test_A_setFoo() {
XCTAssertNil(foo)
foo = 1
XCTAssertNotNil(foo)
}
func test_B_fooIsNotNil() {
XCTAssertNotNil(foo)
}
}
test_A_setFoo()succeeds while test_B_fooIsNotNil() fails

From Flow of Test Execution
(emphasis added):
For each class, testing starts by running the class setup method. For each test method, a new instance of the class is allocated and its instance setup method executed. After that it runs the test method, and after that the instance teardown method. This sequence repeats for all the test methods in the class. After the last test method teardown in the class has been run, Xcode executes the class teardown method and moves on to the next class. This sequence repeats until all the test methods in all test classes have been run.
In your case, test_B_fooIsNotNil() is executed on a fresh instance,
for which the foo property is nil.
Common setup code can be put into the setUp() class method
or setUp() instance method, see
Understanding Setup and Teardown for Test Methods

Related

Shared state in a XCTestCase class?

Can I have shared states in a XCTestCase class?
class Tests: XCTestCase {
var person: Person!
override func setUp() {
person = Person()
}
override func tearDown() {
person = nil
}
func testExample() {
print("✅ testExample")
print(person)
XCTAssertTrue(true)
}
func testExample2() {
print("✅ testExample2")
print(person)
XCTAssertTrue(true)
}
}
For example in the above code I know that person object is different in the different test methods, but is it possible to have same memory location for person object in both the test methods?
There are many popular misconceptions or confusions about how tests work, so I'll just take this opportunity to set the record straight. The memory is managed in a really weird way:
Every time a test method runs, a brand-new instance of the test case class is created, and that instance stays in existence until all the test methods of every test case class have finished.
Thus, you don't need to create the Person object for the person instance variable before every test method (setUp), because you'll get a whole new Tests instance and thus a whole new Person object, before every test method, if you just say
var person: Person! = Person()
But you do need your tearDown to nilify the person instance variable after each test method, because otherwise you'll end up with lots of Person objects. That might not matter for a tiny Person but it's a good habit to get into because you can use a boatload of memory without realizing it otherwise.

CLKComplicationServer initializes an already initialized singleton class CLKComplicationDataSource, although init() is private

To update complications on the watch, I use a singleton class ComplicationController (irrelevant code has been omitted below):
final class ComplicationController: NSObject, CLKComplicationDataSource {
static let shared = ComplicationController() // Instantiate the singleton
private override init() {
super.init()
print("====self: \(self): init")
} // Others can't init the singleton
}
The singleton is created on the watch by the extension delegate:
class ExtensionDelegate: NSObject, WKExtensionDelegate {
override init() {
super.init()
_ = ComplicationController.shared
}
}
When I launch the watch extension with a breakpoint at the print statement above, the execution breaks and the stack trace is:
When I then execute the print statement in a single step, the debugger shows:
====self: <Watch_Extension.ComplicationController: 0x7bf35f20>: init
When I then continue, the execution breaks again at the same breakpoint, and the stack trace is:
After another single step, the debugger shows:
====self: <Watch_Extension.ComplicationController: 0x7d3211d0>: init
Obviously, the CLKComplicationServer has created another instance of the singleton.
My question is: Did I something wrong or is this a bug? If it is a bug, is there a workaround?
PS: It does not help not to initialize ComplicationController in the ExtensionDelegate. In this case, the 2nd instance is created as soon as ComplicationController.shared is used anywhere in the code.
I found a workaround:
Do not instantiate the singleton in the app, i.e. do not use ComplicationController.shared anywhere.
If you have to call functions in ComplicationController.shared, send a notification, e.g. to the default notification center.
The ComplicationController singleton had to have registered for such notifications, and when it receives one, it has to execute the required function.
This did work for me.

Is deinit called when init throws an exception?

Consider the following example class:
class SocketWrapper {
let sock: Int32
init() throws {
try sock = SocketWrapper.createSocket()
}
deinit {
close(sock)
}
}
What happens if createSocket() throws and the init() therefore fails? sock would be left uninitialized. Is deinit still called (on an partially uninitialized object) when init() throws?
deinit is not called on instances that have not been correctly initialized.
If init fails for some reason, then the class instance never starts existing. Therefore, there is no instance on which deinit could be called.
If deinit could be called on a partially initialized instance, it would break the contract of non-optional properties - in your example the socket property would not get assigned and it would still be accessible in deinit as a non-optional but without a value.

Understanding optional global variables in swift

I'm working through a book on Swift and I understand the idea of scope in functions so what I'd like to understand next is why we set global variables using optional types in classes. Honestly it looks like we don't set these variables per say but just let the class know that there will be a variable of a specific type somewhere throughout the code base: var sut: ItemManager!.
From what I understand the variable sut is an unwrapped optional of the type ItemManger which definitely has a valid value and not nil. The reason we've set it with an exclamation mark or question mark is because there isn't an initializer within this class. What isn't clear is since this class doesn't have an initializer, what factors come into play when deciding weather to set this global variable to an optional using a questions mark or implicitly unwrapped with an exclamation mark?
import XCTest
#testable import ToDo
class ItemManagerTests: XCTestCase {
var sut: ItemManager!
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
sut = ItemManager.init()
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func test_ToDoCount_InitiallySetAtZero(){
let sut = ItemManager.init()
XCTAssertEqual(sut.toDoCount, 0)
}
func test_DoneCount_InitiallySetAtZero(){
let sut = ItemManager.init()
XCTAssertEqual(sut.doneCount, 0)
}
}
sut is not a global variable.
sut is an instance variable of the ItemManagerTests. Every time a new instance, i.e. an object, of a ItemManagerTests is instantiated, memory is allocated for sut.
It's of type ItemManager!. This is an implicitly unwrapped optional. It's similar to ItemManager? (a.k.a. Optional), with the distinction that it is implicitly forcefully unwrapped everywhere it's used directly, as if the force unwrap operator (!) was used.
Its value is initialized to nil, but is set to a new ItemManager object when setUp is called by Xcode's testing framework.
Firstly, sut is not a global. A global variable in Swift is declared outside any enclosing scope (So, before the class statement). sut is an instance property; each instance of your ItemManagerTests class will have a sut property.
In Swift, all non-optional properties must be initialised by the time the initialiser has completed. In some cases this can be achieved by code inside the initialiser and in other cases by assigning a default value.
In some cases, s you cannot assign or don't want to assign a default value and you can't (or don't want to) override the initialiser.
If you used a normal optional then the compiler would be satisfied that the property wasn't initialised by default or in the initialiser, but each time you referred to the property you would have to unwrap it (e.g. sut?.donecount).
Since your test case is assigning a value to sut in setup, you know that it will have a value and you could use a force unwrap (e.g. sut!.donecount), or, take it one step further and use an implicitly unwrapped optional. This allows you to refer to the property without any unwrapping, but it is still an optional and will still cause a crash if it is nil.
Note that your current code isn't even using the property, since you are allocating a local variable in your test cases. You can use:
class ItemManagerTests: XCTestCase {
var sut: ItemManager!
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
sut = ItemManager.init()
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func test_ToDoCount_InitiallySetAtZero() {
XCTAssertEqual(sut.toDoCount, 0)
}
func test_DoneCount_InitiallySetAtZero() {
XCTAssertEqual(sut.doneCount, 0)
}
}
The reason we've set it with an exclamation mark or question mark is
because there isn't an initializer within this class
Because when every instance of a class is initialized, it has to allocate memory for its properties/instance var. So with var sut: ItemManager!, the value of sut is nil when init. Without !, ? the compiler can't allocate, init, so you have to init manually in an initializer. That is what compiler told you.
For using ! or ?.
! is used when the property/var always has value after the first assigning. And after the first time it's assigned, it can't be nil later
? is used when the pro/var can have value or not

Swift: overriding an initializer that takes an NSInvocation

I'm trying to create a reusable test harness in Swift with the idea that subclasses will extend the test harness to provide the instance under test, and can add their own subclass-specific test methods, something like this:
class FooTestHarness: XCTestCase {
let instance: Foo
init(instance: Foo) {
self.instance = instance
}
func testFooBehavior() {
XCTAssert(instance.doesFoo())
}
}
class FooPrime: Foo {
func doesFooPrime(): Bool { /* ... */ }
}
class FooPrimeTests: XCTestCase {
init() {
super.init(FooPrime())
}
func myInstance(): FooPrime {
return instance as FooPrime
}
func testFooPrimeBehavior() {
XCTAssert(myInstance().doesFooPrime())
}
}
However, when XCode's testrunner tries to run FooPrimeTests, it doesn't call the no-arg init(), it calls init(invocation: NSInvocation!) (and fails because there isn't one). I tried to override this in FooTestHarness:
init(invocation: NSInvocation!, instance: Foo) {
self.instance = instance
super.init(invocation)
}
and in FooPrimeTests:
init(invocation: NSInvocation!) {
super.init(invocation, FooPrime())
}
but this fails with the message 'NSInvocation' is unavailable.
Is there a workaround?
I'm not os sure if I got it right, but checking the code you suggested you should get a compiler Error like:
Which actually I reckon is quite normal since your FooPrimeTests is just subclassing XCTestCase which has got different init like:
init!(invocation: NSInvocation!)
init!(selector: Selector)
init()
Probably when you posted you're question you're running on an older version of Swift, (I'm currently running it on the Xcode Beta 6.2) that's why you can't see the error. But, and I say again if I got your point right, your class FooPrimeTests can't see you custom initializer just because is sublcassing XCTestCase, rather then FooTestHarness. Which is the class where the init(instance: Foo) is defined.
So you might probably want to define FooPrimeTests as subclass of FooTestHarness. That way you should be able to correctly see your initializer. Hope this help.