6. #SwiftTesting
Unit Tests
★ Prove correctness of different aspects of the
public interface.
• Prove instead of intuition
• Define contract and assumptions
• Document the code
• Easier refactoring or change
★ Reusable code = code + tests
7. #SwiftTesting
Use Unit Testing
Incrementally
★ You don’t have to write every unit test
★ Start with the classes that take care of the
logic
• If mixed apply SOLID
★ The easier entry point is fixing bugs
11. #SwiftTesting
The Rules of Testing
★ We only test our code
★ Only a level of abstraction
★ Only public methods
★ Only one assertion per test
★ Tests are independent of sequence or state
15. #SwiftTesting
Implicitly unwrapped SUT
★ SUT cannot be created in init
★ Thus, it needs to be optional
★ But once set in setUp, it never becomes
nil
★ Syntax is clearer with an implicitly
unwrapped optional.
19. #SwiftTesting
Room for improvement
★ Brian Gesiak: XCTest: The Good Parts:
• Replace/customize Testing frameworks
• XCTAssertThrows
• assert/precondition
• 1,000+ tests
★ I add:
• Run tests without the simulator
• Jon Reid provides a method to speed up AppDelegate launch,
but not for Swift
21. #SwiftTesting
Access control NTFTC
★ It would be nice to have access to internal
properties, but you should only test the public
interface
★ Implicit constructors for structs are internal
★ However, mocks defined in the same test case
can be accessed (internal)
★ If not tested, view controllers may not be
public. But it makes things more complicated.
More on that later.
22. #SwiftTesting
Create your own
templates
import XCTest
import ___PACKAGENAMEASIDENTIFIER___
class ___FILEBASENAMEASIDENTIFIER___: ___VARIABLE_testSubclass___ {
// MARK: - Parameters & Constants
// MARK: - Test vatiables.
var sut: ___VARIABLE_classUnderTest___!
// MARK: - Set up and tear down
override func setUp() {
super.setUp()
createSut()
}
func createSut() {
sut = ___VARIABLE_classUnderTest___()
}
override func tearDown() {
releaseSut()
super.tearDown()
}
func releaseSut() {
sut = nil
}
24. #SwiftTesting
Dependency Injection
★ Code of an object depends on other
objects.
★ Those are considered dependencies.
★ Dependencies must be controlled in order
to reproduce behavior properly.
25. #SwiftTesting
Dependency Injection
★ Extract and override: move to a method
and override in testing class (more fragile)
★ Method injection: change the signature of
the method to provide the dependency
★ Property injection: lazy instantiation
★ Constructor injection: not always possible
26. #SwiftTesting
Stubs & Mocks
★ Both are fake objects
★ Stubs provide desired responses to the
SUT
★ Mocks also expect certain behaviors
28. #SwiftTesting
Testing with dependency
class ViewController: UIViewController {
@IBOutlet weak var messageLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let userDefaults = NSUserDefaults.standardUserDefaults()
let score = userDefaults.integerForKey("PreservedScore")
messageLabel.text = String(score)
}
}
29. #SwiftTesting
func testMessageLabelDisplaysStoredScore() {
var labelMock = LabelMock()
sut.messageLabel = labelMock
sut.userDefaults = UserDefaultsMock()
var view = sut.view
if let text = sut.messageLabel.text {
XCTAssertEqual(text, "1337", "Label must display the
preserved score.")
} else {
XCTFail("Label text must not be nil.")
}
}
class UserDefaultsMock: NSUserDefaults {
override func integerForKey(defaultName: String) ->
Int {
return 1337
}
}
class LabelMock: UILabel {
var presentedText: String?
override internal var text: String? {
get { return presentedText }
set { presentedText = newValue }
}
}
}
Dependency injection
import UIKit
public class ViewController:
UIViewController {
@IBOutlet public weak var
messageLabel: UILabel!
lazy public var userDefaults =
NSUserDefaults.standardUserDefaults()
override public func viewDidLoad()
{
super.viewDidLoad()
let score =
userDefaults.integerForKey("Score")
messageLabel.text =
String(score)
}
}