Presented at the June 2016 Tokyo iOS Meetup on 6/11/2016, in this presentation I give an overview of test driven development and how it can be used when developing for iOS / Swift.
TDD is an enormous topic so this really just scratches the surface to give an expansive glimpse into the journey that TDD truly is. Those unfamiliar with TDD will hopefully have a desire to learn more about it while those familiar will hopefully learn something new.
8. WRITE A TEST
MAKE IT PASS
YADDA YADDA YADDA
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
Challenge Getting over the hump
9. TDD CRASH COURSE
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
Challenge Getting over the hump
10. TESTING WORKFLOW: RED → GREEN → REFACTOR
▸ Write a failing test
▸ Write the simplest implementation
to make it pass
▸ Refactor
▸ Repeat
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
GETTING OVER THE HUMP
11. ▸ Compiler Errors = Failing Test
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
GETTING OVER THE HUMP
TESTING WORKFLOW: RED → GREEN → REFACTOR
12. ▸ Red = Failing Test
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
GETTING OVER THE HUMP
TESTING WORKFLOW: RED → GREEN → REFACTOR
13. ▸ Green = Passing Test
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
GETTING OVER THE HUMP
TESTING WORKFLOW: RED → GREEN → REFACTOR
14. TESTING WORKFLOW: RED → GREEN → REFACTOR
▸ Keyboard Shortcuts for improving your test workflow:
▸ ⌘ + 5 → Show the Xcode Test Navigator
▸ ⇧ + ⌘ + U → Compile Tests
▸ ⌘ + U → Run All Tests In Suite
▸ MAGIC + U → Run all tests for current class
▸ MAGIC + G → Re-run last test
* MAGIC = ⌃ + ⌥ + ⌘
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
GETTING OVER THE HUMP
15. WRITING TESTS TDD STYLE: ARRANGE → ACT → ASSERT
▸ Setup - Create objects needed to execute the test
▸ Action - Prod the subject (object) under test
▸ Verify - Make assertions about your expectations
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
GETTING OVER THE HUMP
16. WRITING TESTS TDD STYLE: ARRANGE → ACT → ASSERT
▸ Override setUp() and tearDown() methods as needed
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
GETTING OVER THE HUMP
17. WRITING TESTS TDD STYLE: ARRANGE → ACT → ASSERT
▸ Clearly indicate where the “Act” portion of your test is:
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
GETTING OVER THE HUMP
18. ▸ Use the appropriate assertion for your test expectation
(XCTest)
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
let number = 11
XCTAssertTrue(number == 12)
error: -[MyProjectTests.MyObjectTest testMethod] : XCTAssertTrue
failed -
GETTING OVER THE HUMP
WRITING TESTS TDD STYLE: ARRANGE → ACT → ASSERT
19. ▸ Use the appropriate assertion for your test expectation
(XCTest)
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
let number = 11
XCTAssertEqual(number, 12)
error: -[MyProjectTests.MyObjectTest testMethod] : XCTAssertEqual
failed: ("Optional(11)") is not equal to ("Optional(12)") -
GETTING OVER THE HUMP
WRITING TESTS TDD STYLE: ARRANGE → ACT → ASSERT
20. ▸ XCT Assertion Types
▸ AssertTrue, AssertFalse
▸ AssertEqual, AssertNotEqual (+WithAccuracy)
▸ AssertLessThan, AssertGreaterThan (+OrEqual)
▸ AssertNil, AssertNotNil
▸ AssertThrowsError
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
GETTING OVER THE HUMP
WRITING TESTS TDD STYLE: ARRANGE → ACT → ASSERT
21. ▸ Nimble (Matcher Framework)
▸ expect(~).to(~) or expect(~).toNot(~)
▸ expect(~).to(beTrue()) or expect(~).to(beFalse())
▸ expect(~).to(beLessThan(~)) // greaterThan…
▸ expect(~).to(beAKindOf(~))
▸ Can also write custom matchers with Nimble
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
GETTING OVER THE HUMP
WRITING TESTS TDD STYLE: ARRANGE → ACT → ASSERT
22. GOLDEN RULES OF TDD
▸ Test First
▸ Simplest Solution
▸ Test Once
▸ Test in Isolation
▸ Test the interface, not the implementation
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
GETTING OVER THE HUMP
23. TDD BEST PRACTICES
▸ One Failing Test at a Time
▸ Create a Test List
▸ Assert First
▸ Simplest Test Data / Evident Test Data
▸ Avoid conditionals and loops
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
GETTING OVER THE HUMP
24. CLASSIC
▸ “Detroit” or “Chicago”
▸ Prefers real objects
▸ Focus: Algorithms
▸ State verification
▸ Test doubles as needed
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
MOCKIST
▸ “London”
▸ Prefers mocks
▸ Focus: Object Interactions
▸ Behavior verification
▸ Test doubles always
GETTING OVER THE HUMP
25. CONTINUOUS INTEGRATION
▸ Build and test in a clean environment
▸ Automation of your test suite
▸ Test across OSs, simulators, devices
▸ Xcode Server, TeamCity, Travis, CircleCI, Jenkins…
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
GETTING OVER THE HUMP
26. CONTINUOUS INTEGRATION - XCODE SERVER BIG SCREEN
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
GETTING OVER THE HUMP
27. CONTINUOUS INTEGRATION - PIVOTAL PROJECT MONITOR
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
GETTING OVER THE HUMP
28. TAKING THE BLUE PILL
▸ BDD
▸ Clean Architecture
▸ SOLID Principles: SRP, Open/Closed, LSP, ISP, DIP
▸ Don’t Repeat Yourself
▸ Just In Time Design
▸ Refactoring
▸ Continuous Integration
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
GETTING OVER THE HUMP
29. MY TESTS TAKE
FOREVER TO RUN
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
Challenge The Time-Consuming Test Run
30. THE TIME-CONSUMING TEST RUN
THE USUAL SUSPECTS
▸ Imbalanced Test Suite
▸ Testing Against External Resources
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
32. TESTING ICE-CREAM CONE ANTI-PATTERN
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
▸ Brittle
▸ Expensive to write/maintain
▸ Time consuming to run
UI & MANUAL TESTS CAN BE…
33. TESTING PYRAMID
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
]
]
ARE WE
BUILDING
THE RIGHT
SYSTEM?
ARE WE
BUILDING
THE SYSTEM
RIGHT?
35. THE TIME-CONSUMING TEST RUN
DESIRED TEST SUITE QUALITIES FOR QUICK TEST FEEDBACK
▸ Few
▸ Fast
▸ Stable
▸ Thorough
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
36. THE TIME-CONSUMING TEST RUN
WHEN TO RUN TESTS
▸ Current Unit/Feature → While building an object / feature
▸ All Units + All Feature → Before commit / push; after
pulling updates from a repo;
▸ Integration, UI → CI Suite
▸ Manual → As needed
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
37. THE TIME-CONSUMING TEST RUN
WHEN TO RUN TESTS
▸ Create a target for each suite of tests that you want to run
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
38. THE TIME-CONSUMING TEST RUN
WHEN TO RUN TESTS
▸ Configure scheme(s) to execute tests:
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
39. THE TIME-CONSUMING TEST RUN
WHEN TO RUN TESTS
▸ Use a makefile with xcodebuild (or xctool) to execute the
tests when you need to or from CI:
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
units:
@xcodebuild -project Osusume.xcodeproj -scheme "Osusume" -sdk
iphonesimulator -destination "platform=iOS Simulator,OS=9.3,name=
iPhone 6" build test
integration:
@xcodebuild -project Osusume.xcodeproj -scheme "Osusume-Staging"
-sdk iphonesimulator -destination "platform=iOS Simulator,OS=9.3,
name=iPhone 6" build test
40. THE TIME-CONSUMING TEST RUN
GUIDANCE ON UNIT TESTING - HOW/WHAT TO TEST
▸ Sandi Metz Rails Conf 2013 “The Magic Tricks of Testing”
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
41. THE TIME-CONSUMING TEST RUN
DO MY TESTS HIT EXTERNAL DEPENDENCIES?
▸ Network, REST APIs
▸ Database (includes Core Data!)
▸ File System
▸ External Library or API
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
42. THE TIME-CONSUMING TEST RUN
THEN HOW TO TEST EXTERNAL DEPENDENCIES?
▸ Find the seams where communication occurs
▸ Confirm expected interactions using mock objects
▸ Create an integration test if needed
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
43. THE TIME-CONSUMING TEST RUN
GUIDANCE ON MOCK OBJECTS
▸ Martin Fowler, “Mocks Aren’t Stubs”
http://martinfowler.com/articles/mocksArentStubs.html
▸ Uncle Bob, “The Little Mocker” Blog Post
https://blog.8thlight.com/uncle-bob/2014/05/14/
TheLittleMocker.html
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
44. MY TEST SUITE
FEELS UNSTABLE
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
Challenge The Brittle Test Suite
45. THE BRITTLE TEST SUITE
THE USUAL SUSPECTS
▸ Highly-coupled objects
▸ Long, complicated, or unreadable tests
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
46. THE BRITTLE TEST SUITE
HIGHLY COUPLED OBJECTS - DESIGN & ARCHITECTURE
▸ TDD encourages us to write loosely coupled components
that can be easily tested in isolation and combined later.
▸ May need to revisit your architecture
▸ “Build & Swap”
▸ Too difficult or too costly to refactor?
▸ TDD a new component and swap it in
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
47. THE BRITTLE TEST SUITE
BUILD & SWAP EXAMPLE #1 - DRUM APP NAVIGATION UX
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
48. THE BRITTLE TEST SUITE
BUILD & SWAP EXAMPLE #2 - MIKADO REFACTOR
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
▸ Parse Integration Before:
▸ One single “God” object singleton: ParseHelper.h/.m
▸ Included in 25 other classes
▸ Parse Integration After:
▸ 4 Dependency-Injected Testable “Repository” objects
▸ Can move each repository over as needed
49. THE BRITTLE TEST SUITE
HOW TDD HELPS
▸ Design
▸ Loosely coupled objects
▸ Well thought out public object APIs
▸ Dev
▸ Gives immediate feedback on quality
▸ Refactoring confidence
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
50. THE BRITTLE TEST SUITE
HOW TO APPROACH
▸ Practice, practice, practice
▸ Reference Materials
▸ Read “Refactoring: Improving the Design of Existing
Code” (Martin Fowler)
▸ Read “Working Effectively With Legacy Code” (Michael
Feathers)
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
51. I DON’T KNOW
HOW TO TEST X
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
Challenge Fear of the Unknown
52. FEAR OF THE UNKNOWN
THE USUAL SUSPECTS
▸ What to test - what not to test
▸ How to test specific objects, dependencies, scenarios
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
53. FEAR OF THE UNKNOWN
KNOWING WHAT NOT TO TEST
▸ Private Methods (this is an implementation detail!)
▸ UI Design (fonts, colors, positions, constraints)
▸ Configuration Details (supporting data)
▸ Auto-generated code
▸ Test Fixtures, Test Doubles
▸ Reference: https://blog.8thlight.com/uncle-bob/
2014/04/30/When-tdd-does-not-work.html
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
54. FEAR OF THE UNKNOWN
HOW TO TEST… VIEW CONTROLLERS
▸ Move logic out of MegaController (*Andy Matuschak -
https://realm.io/news/andy-matuschak-refactor-mega-
controller/)
▸ Using Storyboards:
▸ Property Dependency Injection
▸ Without Storyboards:
▸ Constructor Dependency Injection
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
55. FEAR OF THE UNKNOWN
HOW TO TEST… CORE DATA
▸ Use an in-memory Core Data Store
▸ Only setup data needed for test
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
56. FEAR OF THE UNKNOWN
HOW TO TEST… UITABLEVIEW / UICOLLECTIONVIEW
▸ Extract Datasource and/or Delegate to external object for
easier testing
▸ Leverage blocks / closures for common logic
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
57. FEAR OF THE UNKNOWN
HOW TO TEST… NSUSERDEFAULTS
▸ Swift: Mocks in Swift via Protocols (Blog post, Eli Perkins)
http://blog.eliperkins.me/mocks-in-swift-via-protocols
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
For Objective-C, see OCMock
58. FEAR OF THE UNKNOWN
HOW TO TEST… NSUSERDEFAULTS
▸ Find the method definition(s) on the object that you need
to confirm interactions with:
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
59. FEAR OF THE UNKNOWN
HOW TO TEST… NSUSERDEFAULTS
▸ Create your own protocol definition to duplicate the
method you want to confirm interaction with:
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
60. FEAR OF THE UNKNOWN
HOW TO TEST… NSUSERDEFAULTS
▸ Create a fake object (spy) that implements that protocol:
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
61. FEAR OF THE UNKNOWN
HOW TO TEST… NSUSERDEFAULTS
▸ Write your test:
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
62. FEAR OF THE UNKNOWN
HOW TO TEST… NSUSERDEFAULTS
▸ Write the implementation:
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
63. FEAR OF THE UNKNOWN
HOW TO TEST… NSUSERDEFAULTS
▸ Pass in the real object for production code:
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
64. EASY TO FALL BACK
TO OLD HABITS
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
Challenge Commitment
65. COMMITMENT
THE USUAL SUSPECTS
▸ Frustration, Fatigue
▸ Cutting corners
▸ Falling back to what is most comfortable
▸ Test last or not testing at all
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
66. TOOLS YOU’LL NEED
▸ Focus
▸ Patience
▸ Tenacity
▸ Discipline
▸ Dedication
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
COMMITMENT
67. TIME MANAGEMENT
HOW TO APPROACH
▸ Start Small
▸ 1% each day
▸ Think long-term: Investing in the future of your software
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
68. REWARDS OF STICKING TO IT
▸ Making forward progress in small increments
▸ Allows refactoring to take place with confidence
▸ Identify bugs early and avoid regressions
▸ Reduces costs up-front
▸ Add new features knowing you won’t break existing ones
▸ Deploy anytime with confidence
▸ Easier to understand code for you and your team
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
COMMITMENT