TDD by Controlling Dependencies

Jorge Ortiz
Jorge OrtizOwner at PoWWaU
TDD by Controlling
Dependencies
Jorge D. Ortiz-Fuentes
TDD by Controlling Dependencies
You Go First
TDD by Controlling Dependencies
TDD by Controlling
Dependencies
Jorge D. Ortiz-Fuentes
@jdortiz
#Swift2TDD
A Canonical
Examples
production
#Swift2TDD
#Swift2TDD
Agenda
★ TDD
★ Real Life
★ Swift
TDD
TDD
#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
#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
The TDD Dance
Red
Green
Refactor
#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
TDD Cons?
#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
– Winston Churchill
“Success is stumbling from failure to failure
with no loss of enthusiasm.”
Real
Life
UI Testing
Persistence
Traits
TDD
Swift
+
2
Test the UI
#Swift2TDD
UI Testing
★ Xcode UI Testing ≠ Unit Testing
★ Nor is Kif, Calabash, or Cucumber
(Integration)
★ Unit test is about isolation
View
View Presenter Interactor EntityGateway
Make the view dumb
class 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
}
}
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
}
}
}
Test despite the
persistence
#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
Clean Architecture
View (VC) Presenter
Connector
Interactor Repository
Persistence
WSC
Isolate the user story
class 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)
}
}
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.")
}
Traits
Protocol extensions
protocol 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
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
}
}
Testing a trait (2)
func testPerformSegueWithIdentifierInvokesClassicVersion() {
sut.performSegueWithIdentifier(SegueHandlerTypeViewControlerMock.SegueIdentifier.FirstSeg
ue, 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.FirstSeg
ue, sender: nil)
XCTAssertEqual(sut.identifierToPerformSegue, firstSegueString,
"Identifier must correspond to the raw value of the enum case.")
}
func testPerformSegueWithIdentifierPassesRawValueOfSecondIdentifier() {
sut.performSegueWithIdentifier(SegueHandlerTypeViewControlerMock.SegueIdentifier.SecondSe
gue, sender: nil)
XCTAssertEqual(sut.identifierToPerformSegue, secondSegueString,
"Identifier must correspond to the raw value of the enum case.")
}
– Thomas A. Edison
“I have not failed. I’ve just found 10,000
ways that won’t work.”
Thank
you!
@jdortiz
#Swift2TDD
TDD by Controlling Dependencies
1 of 40

More Related Content

What's hot(20)

Protocols promised-land-2Protocols promised-land-2
Protocols promised-land-2
Michele Titolo329 views
GraphQL API in ClojureGraphQL API in Clojure
GraphQL API in Clojure
Kent Ohashi1.9K views
OTcl and C++ linkages in NS2OTcl and C++ linkages in NS2
OTcl and C++ linkages in NS2
Pradeep Kumar TS4K views
Typescript is the bestTypescript is the best
Typescript is the best
GlobalLogic Ukraine65 views
Typescript is the best by Maxim KryukTypescript is the best by Maxim Kryuk
Typescript is the best by Maxim Kryuk
GlobalLogic Ukraine90 views
Spockを使おう!Spockを使おう!
Spockを使おう!
Takuma Watabiki2.3K views
Groovy AST DemystifiedGroovy AST Demystified
Groovy AST Demystified
Andres Almiray2K views
Annotation processor and compiler pluginAnnotation processor and compiler plugin
Annotation processor and compiler plugin
Oleksandr Radchykov179 views
Introduction to typescriptIntroduction to typescript
Introduction to typescript
Mario Alexandro Santini90 views

Viewers also liked

Vanguard awardVanguard award
Vanguard awardMr. Dan Nwaiwu
190 views1 slide
Vijay_resumeVijay_resume
Vijay_resumevijayganesh06
349 views1 slide
A la boulangieA la boulangie
A la boulangiejamilepsi
341 views1 slide

Viewers also liked(18)

Manual do-laboratórioManual do-laboratório
Manual do-laboratório
Amilton Filho4.3K views
Vanguard awardVanguard award
Vanguard award
Mr. Dan Nwaiwu190 views
E portafolioE portafolio
E portafolio
EVELIN PALAFOX GONZALEZ356 views
Vijay_resumeVijay_resume
Vijay_resume
vijayganesh06349 views
A la boulangieA la boulangie
A la boulangie
jamilepsi341 views
ImformaticaImformatica
Imformatica
Carolina Rios343 views
Doc1Doc1
Doc1
Anisha Nugita214 views
1 fil    prov. especifica  4 bimestre1 fil    prov. especifica  4 bimestre
1 fil prov. especifica 4 bimestre
Felipe Serra194 views
Substance Use DisordersSubstance Use Disorders
Substance Use Disorders
Marius Commodore4.4K views
выпускница школы № 92выпускница школы № 92
выпускница школы № 92
Alexandr Romanovich Salomasov1.6K views
A.b aula 4 amostragemA.b aula 4 amostragem
A.b aula 4 amostragem
Lilian Keila Alves38.6K views
Musu zodisMusu zodis
Musu zodis
read in europe195 views

Similar to TDD by Controlling Dependencies(20)

More from Jorge Ortiz(20)

Unit Test your ViewsUnit Test your Views
Unit Test your Views
Jorge Ortiz222 views
Kata gilded rose en GolangKata gilded rose en Golang
Kata gilded rose en Golang
Jorge Ortiz391 views
CYA: Cover Your AppCYA: Cover Your App
CYA: Cover Your App
Jorge Ortiz343 views
Refactor your way forwardRefactor your way forward
Refactor your way forward
Jorge Ortiz477 views
Architectural superpowersArchitectural superpowers
Architectural superpowers
Jorge Ortiz195 views
To Protect & To ServeTo Protect & To Serve
To Protect & To Serve
Jorge Ortiz925 views
Clean architecture workshopClean architecture workshop
Clean architecture workshop
Jorge Ortiz732 views
Escape from MarsEscape from Mars
Escape from Mars
Jorge Ortiz350 views
Dependence day insurgenceDependence day insurgence
Dependence day insurgence
Jorge Ortiz848 views
Architectural superpowersArchitectural superpowers
Architectural superpowers
Jorge Ortiz333 views
TDD for the massesTDD for the masses
TDD for the masses
Jorge Ortiz706 views
7 Stages of Unit Testing in iOS7 Stages of Unit Testing in iOS
7 Stages of Unit Testing in iOS
Jorge Ortiz805 views
Building for perfectionBuilding for perfection
Building for perfection
Jorge Ortiz685 views
Core Data in Modern TimesCore Data in Modern Times
Core Data in Modern Times
Jorge Ortiz1K views

Recently uploaded(20)

Liqid: Composable CXL PreviewLiqid: Composable CXL Preview
Liqid: Composable CXL Preview
CXL Forum120 views
Java Platform Approach 1.0 - Picnic MeetupJava Platform Approach 1.0 - Picnic Meetup
Java Platform Approach 1.0 - Picnic Meetup
Rick Ossendrijver24 views

TDD by Controlling Dependencies

  • 5. TDD by Controlling Dependencies Jorge D. Ortiz-Fuentes @jdortiz #Swift2TDD
  • 8. TDD
  • 9. TDD
  • 10. #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
  • 11. #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
  • 14. #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
  • 16. #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
  • 17. – Winston Churchill “Success is stumbling from failure to failure with no loss of enthusiasm.”
  • 24. #Swift2TDD UI Testing ★ Xcode UI Testing ≠ Unit Testing ★ Nor is Kif, Calabash, or Cucumber (Integration) ★ Unit test is about isolation
  • 26. Make the view dumb class 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 } }
  • 27. 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 } } }
  • 29. #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
  • 30. Clean Architecture View (VC) Presenter Connector Interactor Repository Persistence WSC
  • 31. Isolate the user story class 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) } }
  • 32. 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.") }
  • 34. Protocol extensions protocol 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
  • 35. 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 } }
  • 36. Testing a trait (2) func testPerformSegueWithIdentifierInvokesClassicVersion() { sut.performSegueWithIdentifier(SegueHandlerTypeViewControlerMock.SegueIdentifier.FirstSeg ue, 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.FirstSeg ue, sender: nil) XCTAssertEqual(sut.identifierToPerformSegue, firstSegueString, "Identifier must correspond to the raw value of the enum case.") } func testPerformSegueWithIdentifierPassesRawValueOfSecondIdentifier() { sut.performSegueWithIdentifier(SegueHandlerTypeViewControlerMock.SegueIdentifier.SecondSe gue, sender: nil) XCTAssertEqual(sut.identifierToPerformSegue, secondSegueString, "Identifier must correspond to the raw value of the enum case.") }
  • 37. – Thomas A. Edison “I have not failed. I’ve just found 10,000 ways that won’t work.”