Download - Swift testing ftw

Transcript
Page 1: Swift testing ftw

Swift Testing FTW!

Jorge D. Ortiz-Fuentes @jdortiz

#SwiftTesting

Page 2: Swift testing ftw

A Canonical Examples

production

#SwiftTesting

Page 3: Swift testing ftw

#SwiftTesting

Agenda

★Basics about unit testing

★4 challenges of Swift Testing

★Proposed enhancements

Page 4: Swift testing ftw

Basics about Unit Testing

Page 5: Swift testing ftw

But my code is always awesome!

Page 6: Swift testing ftw

#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

Page 7: Swift testing ftw

#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

Page 8: Swift testing ftw

Time writing tests < Time debugging

Page 9: Swift testing ftw

Ask for your wishes

Page 10: Swift testing ftw

#SwiftTesting

Types of Unit Tests

★Test return value

★Test state

★Test behavior

Page 11: Swift testing ftw

#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

Page 12: Swift testing ftw

4 Challenges of Swift Testing

Page 13: Swift testing ftw

Lost

Page 14: Swift testing ftw

#SwiftTesting

New to Swift

★Still learning the language

★Functional Paradigm

★Swift has bugs

Page 15: Swift testing ftw

#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.

Page 16: Swift testing ftw

#SwiftTesting

XCTAssertEquals

★Works with non custom objects

★But requires objects to be equatable

★Use reference comparison instead

Page 17: Swift testing ftw

#SwiftTesting

func createSut() { interactor = ShowAllSpeakersInteractorMock() sut = SpeakerListPresenter(interactor: interactor) view = SpeakersListViewMock() sut.view = view } func testViewIsPersisted() { if let persitedView = shut.view as? SpeakersListViewMock { XCTAssertTrue(persistedView === view, “Wrong view persisted”) } else { XCTFail(“View must be persisted”) } }

Example: Test persistence

public class SpeakersListPresenter { let interactor: ShowAllSpeakersInteractorProtocol public weak var view SpeakersListViewProtocol?

public init(interactor: ShowAllSpeakersInteractorProtocol) { self.interactor = interaction } }

Page 18: Swift testing ftw

No Courage

Page 19: Swift testing ftw

#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

Page 20: Swift testing ftw

No Brains

Page 21: Swift testing ftw

#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.

Page 22: Swift testing ftw

#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 }

Page 23: Swift testing ftw

No Heart

Page 24: Swift testing ftw

#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.

Page 25: Swift testing ftw

#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

Page 26: Swift testing ftw

#SwiftTesting

Stubs & Mocks

★Both are fake objects

★Stubs provide desired responses to the SUT

★Mocks also expect certain behaviors

Page 27: Swift testing ftw

OCMock / OCMockito

Not Available!

Page 28: Swift testing ftw

#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) } }

Page 29: Swift testing ftw

#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 injectionimport 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) } }

Page 30: Swift testing ftw

Let the Architecture Help You

Page 31: Swift testing ftw

#SwiftTesting

Clean Architecture

View (VC) Presenter

Wireframe

Interactor Repository

Persistence

WSC

Page 32: Swift testing ftw

Follow the Clean Brick Road

Page 33: Swift testing ftw

canonicalexamples.com coupon:

COCOAHEADSNL

Page 34: Swift testing ftw

Thank you!

Page 35: Swift testing ftw

@jdortiz #CoreDataMT