Top Banner
TDD by Controlling Dependencies Jorge D. Ortiz-Fuentes
40

TDD by Controlling Dependencies

Jan 11, 2017

Download

Technology

Jorge Ortiz
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: TDD by Controlling Dependencies

TDD by Controlling Dependencies

Jorge D. Ortiz-Fuentes

Page 2: TDD by Controlling Dependencies
Page 3: TDD by Controlling Dependencies

You Go First

Page 4: TDD by Controlling Dependencies
Page 5: TDD by Controlling Dependencies

TDD by Controlling Dependencies

Jorge D. Ortiz-Fuentes @jdortiz

#Swift2TDD

Page 6: TDD by Controlling Dependencies

A Canonical Examples

production

#Swift2TDD

Page 7: TDD by Controlling Dependencies

#Swift2TDD

Agenda

★TDD

★Real Life

★Swift

Page 8: TDD by Controlling Dependencies

TDD

Page 9: TDD by Controlling Dependencies

TDD

Page 10: TDD by Controlling Dependencies

#Swift2TDD

Progress as a developer

★ It does things!

★ It does the things I want!

★ I can prove that it does what I say!

★ I am going to make it do exactly what i say!

Nerdvana

Page 11: TDD by Controlling Dependencies

#Swift2TDD

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: TDD by Controlling Dependencies

The TDD Dance

Page 13: TDD by Controlling Dependencies

Red

Green

Refactor

Page 14: TDD by Controlling Dependencies

#Swift2TDD

Pros★100% coverage

★Detect edge cases and unforeseen scenarios

★Measurable progress

★Easier to introduce changes

★ Less debug

★Decoupled code

★Explicit dependencies

★Single responsibility

★Autopilot programming

★Simpler design

Easier

Page 15: TDD by Controlling Dependencies

TDD Cons?

Page 16: TDD by Controlling Dependencies

#Swift2TDD

Cons★Put the horse before the cart

★Takes longer

★ Initial investment. Slower and more work in QA

★Design without the big picture

★Tests must also be rewritten when requirements change

★Other tests are needed too

★Tests are as bad as code

Page 17: TDD by Controlling Dependencies

– Winston Churchill

“Success is stumbling from failure to failure with no loss of enthusiasm.”

Page 18: TDD by Controlling Dependencies

Real Life

Page 19: TDD by Controlling Dependencies

UI Testing

Page 20: TDD by Controlling Dependencies

Persistence

Page 21: TDD by Controlling Dependencies

Traits

Page 22: TDD by Controlling Dependencies

TDD Swift

+2

Page 23: TDD by Controlling Dependencies

Test the UI

Page 24: TDD by Controlling Dependencies

#Swift2TDD

UI Testing

★Xcode UI Testing ≠ Unit Testing

★Nor is Kif, Calabash, or Cucumber (Integration)

★Unit test is about isolation

Page 25: TDD by Controlling Dependencies

View

View Presenter Interactor EntityGateway

Page 26: TDD by Controlling Dependencies

Make the view dumbclass SpeakerTableViewCell: UITableViewCell {

@IBOutlet weak var titleLabel: UILabel! @IBOutlet weak var nameLabel: UILabel! @IBOutlet weak var dateLabel: UILabel! }

extension SpeakerTableViewCell: SpeakerCellProtocol { func displayName(name: String) { nameLabel.text = name }

func displayTitle(title: String) { titleLabel.text = title }

func displayDateSubmitted(date: String){ dateLabel.text = date } }

Page 27: TDD by Controlling Dependencies

Testing a label func testDisplayDateChangesLabelTextToAltValue() { let labelMock = LabelMock() sut.dateLabel = labelMock sut.displayDateSubmitted(speakerAltDateSubmitted) XCTAssertTrue(labelMock.setTextWasInvoked, "displayDate must set the text of the label.") XCTAssertEqual(labelMock.text, speakerAltDateSubmitted, "displayDate must set the text of the label to the provided text.") }

// MARK: - Stubs & Mocks

class LabelMock: UILabel {

// MARK: - Properties

var setTextWasInvoked = false override var text: String? { didSet { setTextWasInvoked = true } } }

Page 28: TDD by Controlling Dependencies

Test despite the persistence

Page 29: TDD by Controlling Dependencies

#Swift2TDD

Testing Business Logic

★Persistence seems to be a requirement for testing the logic

★But it can be abstracted

★And the data from the entity gateway

Page 30: TDD by Controlling Dependencies

Clean Architecture

View (VC) Presenter

Connector

Interactor Repository

Persistence

WSC

Page 31: TDD by Controlling Dependencies

Isolate the user storyclass ShowAllSpeakersInteractor {

// MARK: - Properties

let entityGateway: EntityGatewayProtocol weak var presenter: SpeakersListPresenterProtocol?

// MARK: - Initializers

init(entityGateway: EntityGatewayProtocol) { self.entityGateway = entityGateway } }

extension ShowAllSpeakersInteractor: ShowAllSpeakersInteractorProtocol { func showAllSpeakers() { let entities = entityGateway.fetchAllSpeakers() let displayData = entities.map({entity in return SpeakerDisplayData(speaker: entity)}) presenter?.presentAllSpeakers(displayData) } }

Page 32: TDD by Controlling Dependencies

Testing a use case func testEntityIstransformedIntoDisplayData() { entityGateway.entities = [Speaker(name: speakerMainName, title: speakerMainTitle, synopsis: speakerMainSynopsis, dateSubmitted: speakerMainDateSubmitted)]

sut.showAllSpeakers()

guard presenter.displayData.count == entityGateway.entities.count else { XCTFail(“There must be one and only one display data per each entity of the repo.”) return }

let speakerData = presenter.displayData[0] XCTAssertEqual(speakerData.name, speakerMainName, "Speaker name must be copied onto the display data.") XCTAssertEqual(speakerData.title, speakerMainTitle, "Speaker title must be copied onto the display data.") XCTAssertEqual(speakerData.dateSubmitted, speakerMainDateSubmitted, "Speaker submitedDate must be copied onto the display data.") }

Page 33: TDD by Controlling Dependencies

Traits

Page 34: TDD by Controlling Dependencies

Protocol extensionsprotocol SegueHandlerTypeProtocol { typealias SegueIdentifier: RawRepresentable }

extension SegueHandlerTypeProtocol where Self: UIViewController, SegueIdentifier.RawValue == String { func performSegueWithIdentifier(segueIdentifier: SegueIdentifier, sender: AnyObject?) { performSegueWithIdentifier(segueIdentifier.rawValue, sender: sender) }

func segueIdentifierForSegue(segue: UIStoryboardSegue) -> SegueIdentifier { guard let identifier = segue.identifier, segueIdentifier = SegueIdentifier(rawValue: identifier) else { fatalError("Invalid segue identifier \(segue.identifier).") } return segueIdentifier } }

As explained in WWDC 2015 Swift in Practice

Page 35: TDD by Controlling Dependencies

Testing a Trait var sut: SegueHandlerTypeViewControlerMock!

class SegueHandlerTypeViewControlerMock: UIViewController, SegueHandlerTypeProtocol {

// MARK: - Properties var identifierToPerformSegue: String? var senderPerformingSegue: AnyObject?

enum SegueIdentifier: String { case FirstSegue = "FirstSegueString" case SecondSegue = "SecondSegueString" }

// MARK: - Mocks & Stubs override func performSegueWithIdentifier(identifier: String, sender: AnyObject?) { identifierToPerformSegue = identifier senderPerformingSegue = sender } }

Page 36: TDD by Controlling Dependencies

Testing a trait (2) func testPerformSegueWithIdentifierInvokesClassicVersion() { sut.performSegueWithIdentifier(SegueHandlerTypeViewControlerMock.SegueIdentifier.FirstSegue, sender: nil) XCTAssertNotNil(sut.identifierToPerformSegue, "Classic version of performSegueWithIdentifier must be invoked by the one in the protocol extension.") }

func testPerformSegueWithIdentifierPassesRawValueOfFirstIdentifier() { sut.performSegueWithIdentifier(SegueHandlerTypeViewControlerMock.SegueIdentifier.FirstSegue, sender: nil) XCTAssertEqual(sut.identifierToPerformSegue, firstSegueString, "Identifier must correspond to the raw value of the enum case.") } func testPerformSegueWithIdentifierPassesRawValueOfSecondIdentifier() { sut.performSegueWithIdentifier(SegueHandlerTypeViewControlerMock.SegueIdentifier.SecondSegue, sender: nil) XCTAssertEqual(sut.identifierToPerformSegue, secondSegueString, "Identifier must correspond to the raw value of the enum case.") }

Page 37: TDD by Controlling Dependencies

– Thomas A. Edison

“I have not failed. I’ve just found 10,000 ways that won’t work.”

Page 38: TDD by Controlling Dependencies

Thank you!

Page 39: TDD by Controlling Dependencies

@jdortiz #Swift2TDD

Page 40: TDD by Controlling Dependencies