50
7 Stages of Unit Testing in iOS Jorge D. Ortiz-Fuentes @jdortiz #7SUnitTest

7 Stages of Unit Testing in iOS

Embed Size (px)

Citation preview

7 Stages of Unit Testing in iOS

Jorge D. Ortiz-Fuentes @jdortiz

#7SUnitTest

A Canonical Examples production

#7SUnitTest

#7SUnitTest

Agenda

Evangelize about unit tests to seasoned programmers

Introduce them to everybody else

1.

Shock & Disbelief

But my code is always awesome!

#7SUnitTest

Unit TestsProve correctness of different aspects of the public interface.

• Prove instead of intuition

• Define contract and assumptions

• Document the code

• Easier refactoring or change

Reusable code = code + tests

Test the parts that you aren’t developing now

#7SUnitTest

Use Unit Testing Incrementally

You don’t have to write every unit test

Start with the classes that take care of the logic

• If mixed apply SOLID

The easier entry point are bugs

2.

Denial

C’mon, it takes ages to write

tests!

Time writing tests < Time debugging

#7SUnitTest

Good & Bad NewsMost already available in Xcode / not doubles

Projects are created with

• test target

• a crappy example

⌘+U is your friend

Behaviors provide Pavlov reinforcement

XCTest is THE tool for the rest you are on your own

3.

Anger

How do I write those f***ing

tests?

#7SUnitTest

Types of Unit Tests

Test return value

Test state

Test behavior

#7SUnitTest

The SUT

@interface Thermostat : NSObject

@property (strong, nonatomic) NSNumber *setpoint; @property (assign, nonatomic) BOOL imperialSystem;

- (NSString *) showSetpoint; - (void) increaseSetpoint; - (void) saveSetpoint;

@end

#7SUnitTest

Return

- (NSString *) showSetpoint { return [NSString stringWithFormat:@"%@ %@", self.setpoint, self.imperialSystem?@"F":@"C"]; }

#7SUnitTest

Test return

- (void) testSetpointIsShownWithTheRightUnits { // Arrange sut.setpoint = @23; sut.imperialSystem = NO;

// Act NSString *shownSetpoint = [sut showSetpoint];

// Assert XCTAssertEqualObjects(shownSetpoint, @"23 C"); }

#7SUnitTest

State

- (void) increaseSetpoint { self.setpoint = @([self.setpoint intValue] + 1); }

#7SUnitTest

Test state

- (void) testIncreaseSetpointAddsOne { // Arrange sut.setpoint = @23;

// Act [sut increaseSetpoint];

// Assert XCTAssertEqualObjects(sut.setpoint, @24); }

#7SUnitTest

Behavior@interface Thermostat() @property (strong, nonatomic) NSUserDefaults *userDefaults; @end

@implementation Thermostat - (void) saveSetpoint { [self.userDefaults setObject:self.setpoint forKey:@"setpoint"]; }

- (NSUserDefaults *) userDefaults { if (_userDefaults == nil) { _userDefaults = [NSUserDefaults standardUserDefaults]; } return _userDefaults; } @end

#7SUnitTest

Create a Spy@interface UserDefaultsMock: NSUserDefaults

@property (assign, nonatomic) BOOL valueWasSet;

@end

@implementation UserDefaultsMock

- (void)setValue:(id)value forKey:(NSString *)key { self.valueWasSet = YES; }

@end

#7SUnitTest

Test behavior

- (void) testSetpointIsPersisted { // Arrange sut.setpoint = @23; UserDefaultsMock *userDefMock = [UserDefaultsMock new]; [sut setValue:userDefMock forKey:@"userDefaults"]; // Act [sut saveSetpoint];

// Assert XCTAssertTrue(userDefMock.valueWasSet); }

4.

Bargain

Ok, I’ll write some tests, but

make my life simpler

- (void) testSetpointIsPersisted { // Arrange sut.setpoint = @23; id userDefMock = [OCMockObject mockForClass:[NSUserDefaults class]]; OCMExpect([userDefMock setObject:sut.setpoint forKey:@"setpoint"]); [sut setValue:userDefMock forKey:@"userDefaults"]; // Act [sut saveSetpoint]; // Assert XCTAssertNoThrow([userDefMock verify]); }

Simulating and Testing Behavior

Stubs & Mocks

• Both are test doubles

• Stubs provide desired responses to the SUT

• Spies also expect certain behaviors

• Mocks include verification

#7SUnitTest

Use a Mocking Framework

Install as a pod

• OCMock

• OCMockito

Not for Swift 😱

#7SUnitTest

Dependency Injection

Control behavior of the dependencies

• Constructor

• Method overwriting

• Property injection:Lazy instantiation

Or use a DI framework (uncommon)

#7SUnitTest

An Assertion Framework

Richer semantics

• OCHamcrest

• Expecta

• Nimble

5.

Guilt

My tests are never good

enough!

#7SUnitTest

Follow the rules

Test your code only

Only a level of abstraction

Only public methods

Only one assertion per test

Tests are independent of sequence or state

6.

Depression

But my coverage is poor

#7SUnitTest

Improve your coverage

Measure it

Assign priorities

Agree upon rules for your team/project

Coverage

Improve

#7SUnitTest

And when I have too many tests?

Disable test cases

Run from command line

• xcodebuild

• xcpretty

7.

Acceptance & Hope

No tests, no fun

#7SUnitTest

Evolve

Clean Architecture

TDD

Other tests: integration, ui, performance…

CI (OSX Server / Jenkins)

Thank you!

@jdortiz #7SUnitTest