TEST DRIVEN DEVELOPMENT COMMON CHALLENGES & BEST PRACTICES FOR Tokyo iOS Meetup June 2016 Derek Lee @derekleerock ON IOS
TEST DRIVEN DEVELOPMENT
COMMON CHALLENGES & BEST PRACTICES FOR
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
ON IOS
COMMON PITFALLS & BEST PRACTICES FOR TDD
YOUR PRESENTER
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
4/2015~11/2015
12/2015~
1/2014~
WHY TDD?Tokyo iOS Meetup June 2016Derek Lee @derekleerock
T D D
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
Question What is TDD?
TEST DRIVEN DEVELOPMENT
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
Question What is TDD?
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
WRITE A TEST MAKE IT PASS YADDA YADDA YADDA
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
Challenge Getting over the hump
TDD CRASH COURSETokyo iOS Meetup June 2016Derek Lee @derekleerock
Challenge Getting over the hump
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
▸ Compiler Errors = Failing Test
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
GETTING OVER THE HUMP
TESTING WORKFLOW: RED → GREEN → REFACTOR
▸ Red = Failing Test
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
GETTING OVER THE HUMP
TESTING WORKFLOW: RED → GREEN → REFACTOR
▸ Green = Passing Test
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
GETTING OVER THE HUMP
TESTING WORKFLOW: RED → GREEN → REFACTOR
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
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
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
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
▸ 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
▸ 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
▸ 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
▸ 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
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
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
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
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
CONTINUOUS INTEGRATION - XCODE SERVER BIG SCREEN
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
GETTING OVER THE HUMP
CONTINUOUS INTEGRATION - PIVOTAL PROJECT MONITOR
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
GETTING OVER THE HUMP
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
MY TESTS TAKE FOREVER TO RUN
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
Challenge The Time-Consuming Test Run
THE TIME-CONSUMING TEST RUN
THE USUAL SUSPECTS
▸ Imbalanced Test Suite
▸ Testing Against External Resources
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
TESTING ICE-CREAM CONE ANTI-PATTERN
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
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…
TESTING PYRAMID
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
]] ARE WE
BUILDING THE RIGHT SYSTEM?
ARE WE BUILDING
THE SYSTEM RIGHT?
TESTING PYRAMID
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
TIM
E TO
TES
T IN
CREA
SES
THE TIME-CONSUMING TEST RUN
DESIRED TEST SUITE QUALITIES FOR QUICK TEST FEEDBACK
▸ Few
▸ Fast
▸ Stable
▸ Thorough
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
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
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
THE TIME-CONSUMING TEST RUN
WHEN TO RUN TESTS
▸ Configure scheme(s) to execute tests:
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
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
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
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
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
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
MY TEST SUITE FEELS UNSTABLE
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
Challenge The Brittle Test Suite
THE BRITTLE TEST SUITE
THE USUAL SUSPECTS
▸ Highly-coupled objects
▸ Long, complicated, or unreadable tests
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
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
THE BRITTLE TEST SUITE
BUILD & SWAP EXAMPLE #1 - DRUM APP NAVIGATION UX
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
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
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
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
I DON’T KNOW HOW TO TEST X
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
Challenge Fear of the Unknown
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
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
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
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
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
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
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
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
FEAR OF THE UNKNOWN
HOW TO TEST… NSUSERDEFAULTS
▸ Create a fake object (spy) that implements that protocol:
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
FEAR OF THE UNKNOWN
HOW TO TEST… NSUSERDEFAULTS
▸ Write your test:
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
FEAR OF THE UNKNOWN
HOW TO TEST… NSUSERDEFAULTS
▸ Write the implementation:
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
FEAR OF THE UNKNOWN
HOW TO TEST… NSUSERDEFAULTS
▸ Pass in the real object for production code:
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
EASY TO FALL BACK TO OLD HABITS
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
Challenge Commitment
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
TOOLS YOU’LL NEED
▸ Focus
▸ Patience
▸ Tenacity
▸ Discipline
▸ Dedication
Tokyo iOS Meetup June 2016Derek Lee @derekleerock
COMMITMENT
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
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
@DEREKLEEROCK
Thank you!
Tokyo iOS Meetup June 2016Derek Lee @derekleerock