Common Challenges & Best Practices for TDD on iOS

70
TEST DRIVEN DEVELOPMENT COMMON CHALLENGES & BEST PRACTICES FOR Tokyo iOS Meetup June 2016 Derek Lee @derekleerock ON IOS

Transcript of Common Challenges & Best Practices for TDD on iOS

Page 1: Common Challenges & Best Practices for TDD on iOS

TEST DRIVEN DEVELOPMENT

COMMON CHALLENGES & BEST PRACTICES FOR

Tokyo iOS Meetup June 2016Derek Lee @derekleerock

ON IOS

Page 2: Common Challenges & Best Practices for TDD 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~

Page 3: Common Challenges & Best Practices for TDD on iOS
Page 4: Common Challenges & Best Practices for TDD on iOS

WHY TDD?Tokyo iOS Meetup June 2016Derek Lee @derekleerock

Page 5: Common Challenges & Best Practices for TDD on iOS

T D D

Tokyo iOS Meetup June 2016Derek Lee @derekleerock

Question What is TDD?

Page 6: Common Challenges & Best Practices for TDD on iOS

TEST DRIVEN DEVELOPMENT

Tokyo iOS Meetup June 2016Derek Lee @derekleerock

Question What is TDD?

Page 7: Common Challenges & Best Practices for TDD on iOS

Tokyo iOS Meetup June 2016Derek Lee @derekleerock

Page 8: Common Challenges & Best Practices for TDD on iOS

WRITE A TEST MAKE IT PASS YADDA YADDA YADDA

Tokyo iOS Meetup June 2016Derek Lee @derekleerock

Challenge Getting over the hump

Page 9: Common Challenges & Best Practices for TDD on iOS

TDD CRASH COURSETokyo iOS Meetup June 2016Derek Lee @derekleerock

Challenge Getting over the hump

Page 10: Common Challenges & Best Practices for TDD on iOS

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

Page 11: Common Challenges & Best Practices for TDD on iOS

▸ Compiler Errors = Failing Test

Tokyo iOS Meetup June 2016Derek Lee @derekleerock

GETTING OVER THE HUMP

TESTING WORKFLOW: RED → GREEN → REFACTOR

Page 12: Common Challenges & Best Practices for TDD on iOS

▸ Red = Failing Test

Tokyo iOS Meetup June 2016Derek Lee @derekleerock

GETTING OVER THE HUMP

TESTING WORKFLOW: RED → GREEN → REFACTOR

Page 13: Common Challenges & Best Practices for TDD on iOS

▸ Green = Passing Test

Tokyo iOS Meetup June 2016Derek Lee @derekleerock

GETTING OVER THE HUMP

TESTING WORKFLOW: RED → GREEN → REFACTOR

Page 14: Common Challenges & Best Practices for TDD on iOS

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

Page 15: Common Challenges & Best Practices for TDD on iOS

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

Page 16: Common Challenges & Best Practices for TDD on iOS

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

Page 17: Common Challenges & Best Practices for TDD on iOS

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

Page 18: Common Challenges & Best Practices for TDD on iOS

▸ 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

Page 19: Common Challenges & Best Practices for TDD on iOS

▸ 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

Page 20: Common Challenges & Best Practices for TDD on iOS

▸ 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

Page 21: Common Challenges & Best Practices for TDD on iOS

▸ 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

Page 22: Common Challenges & Best Practices for TDD on iOS

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

Page 23: Common Challenges & Best Practices for TDD on iOS

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

Page 24: Common Challenges & Best Practices for TDD on iOS

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

Page 25: Common Challenges & Best Practices for TDD on iOS

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

Page 26: Common Challenges & Best Practices for TDD on iOS

CONTINUOUS INTEGRATION - XCODE SERVER BIG SCREEN

Tokyo iOS Meetup June 2016Derek Lee @derekleerock

GETTING OVER THE HUMP

Page 27: Common Challenges & Best Practices for TDD on iOS

CONTINUOUS INTEGRATION - PIVOTAL PROJECT MONITOR

Tokyo iOS Meetup June 2016Derek Lee @derekleerock

GETTING OVER THE HUMP

Page 28: Common Challenges & Best Practices for TDD on iOS

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

Page 29: Common Challenges & Best Practices for TDD on iOS

MY TESTS TAKE FOREVER TO RUN

Tokyo iOS Meetup June 2016Derek Lee @derekleerock

Challenge The Time-Consuming Test Run

Page 30: Common Challenges & Best Practices for TDD on iOS

THE TIME-CONSUMING TEST RUN

THE USUAL SUSPECTS

▸ Imbalanced Test Suite

▸ Testing Against External Resources

Tokyo iOS Meetup June 2016Derek Lee @derekleerock

Page 31: Common Challenges & Best Practices for TDD on iOS

TESTING ICE-CREAM CONE ANTI-PATTERN

Tokyo iOS Meetup June 2016Derek Lee @derekleerock

Page 32: Common Challenges & Best Practices for TDD on iOS

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…

Page 33: Common Challenges & Best Practices for TDD on iOS

TESTING PYRAMID

Tokyo iOS Meetup June 2016Derek Lee @derekleerock

]] ARE WE

BUILDING THE RIGHT SYSTEM?

ARE WE BUILDING

THE SYSTEM RIGHT?

Page 34: Common Challenges & Best Practices for TDD on iOS

TESTING PYRAMID

Tokyo iOS Meetup June 2016Derek Lee @derekleerock

TIM

E TO

TES

T IN

CREA

SES

Page 35: Common Challenges & Best Practices for TDD on iOS

THE TIME-CONSUMING TEST RUN

DESIRED TEST SUITE QUALITIES FOR QUICK TEST FEEDBACK

▸ Few

▸ Fast

▸ Stable

▸ Thorough

Tokyo iOS Meetup June 2016Derek Lee @derekleerock

Page 36: Common Challenges & Best Practices for TDD on iOS

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

Page 37: Common Challenges & Best Practices for TDD on iOS

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

Page 38: Common Challenges & Best Practices for TDD on iOS

THE TIME-CONSUMING TEST RUN

WHEN TO RUN TESTS

▸ Configure scheme(s) to execute tests:

Tokyo iOS Meetup June 2016Derek Lee @derekleerock

Page 39: Common Challenges & Best Practices for TDD on iOS

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

Page 40: Common Challenges & Best Practices for TDD on iOS

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

Page 41: Common Challenges & Best Practices for TDD on iOS

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

Page 42: Common Challenges & Best Practices for TDD on iOS

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

Page 43: Common Challenges & Best Practices for TDD on iOS

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

Page 44: Common Challenges & Best Practices for TDD on iOS

MY TEST SUITE FEELS UNSTABLE

Tokyo iOS Meetup June 2016Derek Lee @derekleerock

Challenge The Brittle Test Suite

Page 45: Common Challenges & Best Practices for TDD on iOS

THE BRITTLE TEST SUITE

THE USUAL SUSPECTS

▸ Highly-coupled objects

▸ Long, complicated, or unreadable tests

Tokyo iOS Meetup June 2016Derek Lee @derekleerock

Page 46: Common Challenges & Best Practices for TDD on iOS

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

Page 47: Common Challenges & Best Practices for TDD on iOS

THE BRITTLE TEST SUITE

BUILD & SWAP EXAMPLE #1 - DRUM APP NAVIGATION UX

Tokyo iOS Meetup June 2016Derek Lee @derekleerock

Page 48: Common Challenges & Best Practices for TDD on iOS

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

Page 49: Common Challenges & Best Practices for TDD on iOS

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

Page 50: Common Challenges & Best Practices for TDD on iOS

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

Page 51: Common Challenges & Best Practices for TDD on iOS

I DON’T KNOW HOW TO TEST X

Tokyo iOS Meetup June 2016Derek Lee @derekleerock

Challenge Fear of the Unknown

Page 52: Common Challenges & Best Practices for TDD on iOS

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

Page 53: Common Challenges & Best Practices for TDD on iOS

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

Page 54: Common Challenges & Best Practices for TDD on iOS

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

Page 55: Common Challenges & Best Practices for TDD on iOS

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

Page 56: Common Challenges & Best Practices for TDD on iOS

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

Page 57: Common Challenges & Best Practices for TDD on iOS

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

Page 58: Common Challenges & Best Practices for TDD on iOS

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

Page 59: Common Challenges & Best Practices for TDD on iOS

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

Page 60: Common Challenges & Best Practices for TDD on iOS

FEAR OF THE UNKNOWN

HOW TO TEST… NSUSERDEFAULTS

▸ Create a fake object (spy) that implements that protocol:

Tokyo iOS Meetup June 2016Derek Lee @derekleerock

Page 61: Common Challenges & Best Practices for TDD on iOS

FEAR OF THE UNKNOWN

HOW TO TEST… NSUSERDEFAULTS

▸ Write your test:

Tokyo iOS Meetup June 2016Derek Lee @derekleerock

Page 62: Common Challenges & Best Practices for TDD on iOS

FEAR OF THE UNKNOWN

HOW TO TEST… NSUSERDEFAULTS

▸ Write the implementation:

Tokyo iOS Meetup June 2016Derek Lee @derekleerock

Page 63: Common Challenges & Best Practices for TDD on iOS

FEAR OF THE UNKNOWN

HOW TO TEST… NSUSERDEFAULTS

▸ Pass in the real object for production code:

Tokyo iOS Meetup June 2016Derek Lee @derekleerock

Page 64: Common Challenges & Best Practices for TDD on iOS

EASY TO FALL BACK TO OLD HABITS

Tokyo iOS Meetup June 2016Derek Lee @derekleerock

Challenge Commitment

Page 65: Common Challenges & Best Practices for TDD on iOS

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

Page 66: Common Challenges & Best Practices for TDD on iOS

TOOLS YOU’LL NEED

▸ Focus

▸ Patience

▸ Tenacity

▸ Discipline

▸ Dedication

Tokyo iOS Meetup June 2016Derek Lee @derekleerock

COMMITMENT

Page 67: Common Challenges & Best Practices for TDD on iOS

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

Page 68: Common Challenges & Best Practices for TDD on iOS

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

Page 69: Common Challenges & Best Practices for TDD on iOS
Page 70: Common Challenges & Best Practices for TDD on iOS

@DEREKLEEROCK

Thank you!

Tokyo iOS Meetup June 2016Derek Lee @derekleerock