Download - Tdd iPhone For Dummies
Does it work?
I’m not here to convince you
Dry and easy to maintain
Better form of documentation
Protect
Tdd?
Test-driven development (TDD) is a software development technique that relies on the repetition of a very short development cycle
Tdd?
http://en.wikipedia.org/wiki/Test-driven_development
first the developer writes a failing automated test case that defines a desired improvement or new function
Tdd?
http://en.wikipedia.org/wiki/Test-driven_development
then produces code to pass that test
Tdd?
http://en.wikipedia.org/wiki/Test-driven_development
and finally refactors the new code to acceptable standard
Tdd?
http://en.wikipedia.org/wiki/Test-driven_development
Unit Tests?
A test is not a unit test if:
Michael Feathers
Unit Tests?
It talks to a database
Michael Feathers
Unit Tests?
It communicates across the network
Michael Feathers
Unit Tests?
It touches the file system
Michael Feathers
Unit Tests?
You have to do things to your environment to run it (eg, change config files)
Michael Feathers
Unit Tests?
Tests that do this are integration tests
Michael Feathers
Steps
Fixture Setup
Automatic Testing Lifecycle
Steps
Fixture Setup
Exercise SUT
Automatic Testing Lifecycle
SUT
Steps
Fixture Setup
Exercise SUT
Verify Result
Automatic Testing Lifecycle
SUT
Steps
Fixture Setup
Exercise SUT
Verify Result
Fixture TearDown
Automatic Testing Lifecycle
What about iPhone Dev?
Unit Testing for iPhone
OCUnit
Former SenTestingKit
#import <SenTestingKit/SenTestingKit.h>#import "RpnCalculator.h"
@interface RpnCalculatorTestCase : SenTestCase {! RpnCalculator* rpnCalculator;}
@end
TestCase definition
#import <SenTestingKit/SenTestingKit.h>#import "RpnCalculator.h"
@interface RpnCalculatorTestCase : SenTestCase {! RpnCalculator* rpnCalculator;}
@end
TestCase definition
@implementation RpnCalculatorTestCase
-(void)setUp{! rpnCalculator = [[RpnCalculator alloc]init];}
-(void)testShouldDisplayTwoNumbersWhenNumbersAreSeparatedByEnter{! [rpnCalculator put:@"1"];! [rpnCalculator put:@"enter"];! [rpnCalculator put:@"2"];!!! STAssertEqualObjects(@"1\n2",rpnCalculator.display,nil);}
-(void)tearDown{ [rpnCalculator release];}
TestCase Implementation
@implementation RpnCalculatorTestCase
-(void)setUp{! rpnCalculator = [[RpnCalculator alloc]init];}
-(void)testShouldDisplayTwoNumbersWhenNumbersAreSeparatedByEnter{! [rpnCalculator put:@"1"];! [rpnCalculator put:@"enter"];! [rpnCalculator put:@"2"];!!! STAssertEqualObjects(@"1\n2",rpnCalculator.display,nil);}
-(void)tearDown{ [rpnCalculator release];}
Fixture Setup
@implementation RpnCalculatorTestCase
-(void)setUp{! rpnCalculator = [[RpnCalculator alloc]init];}
-(void)testShouldDisplayTwoNumbersWhenNumbersAreSeparatedByEnter{! [rpnCalculator put:@"1"];! [rpnCalculator put:@"enter"];! [rpnCalculator put:@"2"];!!! STAssertEqualObjects(@"1\n2",rpnCalculator.display,nil);}
-(void)tearDown{ [rpnCalculator release];}
Exercise SUT
@implementation RpnCalculatorTestCase
-(void)setUp{! rpnCalculator = [[RpnCalculator alloc]init];}
-(void)testShouldDisplayTwoNumbersWhenNumbersAreSeparatedByEnter{! [rpnCalculator put:@"1"];! [rpnCalculator put:@"enter"];! [rpnCalculator put:@"2"];!!! STAssertEqualObjects(@"1\n2",rpnCalculator.display,nil);}
-(void)tearDown{ [rpnCalculator release];}
Verify Result
@implementation RpnCalculatorTestCase
-(void)setUp{! rpnCalculator = [[RpnCalculator alloc]init];}
-(void)testShouldDisplayTwoNumbersWhenNumbersAreSeparatedByEnter{! [rpnCalculator put:@"1"];! [rpnCalculator put:@"enter"];! [rpnCalculator put:@"2"];!!! STAssertEqualObjects(@"1\n2",rpnCalculator.display,nil);}
-(void)tearDown{ [rpnCalculator release];}
Fixture Teardown
#define STAssertNil(a1, description, ...)#define STAssertNotNil(a1, description, ...)#define STAssertTrue(expression, description, ...)#define STAssertFalse(expression, description, ...)#define STAssertEqualObjects(a1, a2, description, ...)#define STAssertEquals(a1, a2, description, ...)#define STFail(description, ...)#define STAssertTrueNoThrow(expression, description, ...)#define STAssertFalseNoThrow(expression, description, ...)
//....
Assertions
Xcode integration
Xcode integration
Presentation separate from logic
Presentation separate from logic
Presentation
Logic
Test
Xcode integration
Xcode integration
What if I have to access network...
Or I have correlated components..
Mock it!
Mock it!
[ . . . ] mock objects are simulated objects that mimic the behavior of real objects in controlled ways
OCMock
http://www.mulle-kybernetik.com/software/OCMock/
Stubs vs Mocks
Stubs
Mocks
- (void)testReturnsStubbedReturnValue{! mock = [OCMockObject mockForClass:[NSString class]];
[[[mock stub] andReturn:@"megamock"] lowercaseString]; id returnValue = [mock lowercaseString];! STAssertEqualObjects(@"megamock", returnValue, nil); }
Stub
SOAPMessage
XMLString
SOAPChannel
post
SOAPClient
send
Collaboration
SOAPMessage
XMLString
SOAPChannel
post
SOAPClient
send
Collaboration
SOAPMessage
XMLString
SOAPChannel
post
SOAPClient
send
Collaboration
static const NSString * RawSOAPMessage = @"<ENV:evn xmlns:ENV="http://www.w3.org/....
- (void)testClientShouldSendMessage{! id msgMock = [OCMockObject mockForClass:[SOAPMessage class]];! [[[msgMock expect] andReturn:RawSOAPMessage] XMLString];
! id channelMock = [OCMockObject mockForClass:[SOAPChannel class]];! [[[channelMock expect] andReturn:TRUE] send];
! SOAPClient *client = [SOAPClient initWith:channelMock];!! [client sendMessage:msgMock];
! [msgMock verify];! [channelMock verify];}
Mock
static const NSString * RawSOAPMessage = @"<ENV:evn xmlns:ENV="http://www.w3.org/....
- (void)testClientShouldSendMessage{! id msgMock = [OCMockObject mockForClass:[SOAPMessage class]];! [[[msgMock expect] andReturn:RawSOAPMessage] XMLString];
! id channelMock = [OCMockObject mockForClass:[SOAPChannel class]];! [[[channelMock expect] andReturn:TRUE] send];
! SOAPClient *client = [SOAPClient initWith:channelMock];!! [client sendMessage:msgMock];
! [msgMock verify];! [channelMock verify];}
Mockery
static const NSString * RawSOAPMessage = @"<ENV:evn xmlns:ENV="http://www.w3.org/....
- (void)testClientShouldSendMessage{! id msgMock = [OCMockObject mockForClass:[SOAPMessage class]];! [[[msgMock expect] andReturn:RawSOAPMessage] XMLString];
! id channelMock = [OCMockObject mockForClass:[SOAPChannel class]];! [[[channelMock expect] andReturn:TRUE] send];
! SOAPClient *client = [SOAPClient initWith:channelMock];!! [client sendMessage:msgMock];
! [msgMock verify];! [channelMock verify];}
Exercise SUT
static const NSString * RawSOAPMessage = @"<ENV:evn xmlns:ENV="http://www.w3.org/....
- (void)testClientShouldSendMessage{! id msgMock = [OCMockObject mockForClass:[SOAPMessage class]];! [[[msgMock expect] andReturn:RawSOAPMessage] XMLString];
! id channelMock = [OCMockObject mockForClass:[SOAPChannel class]];! [[[channelMock expect] andReturn:TRUE] send];
! SOAPClient *client = [SOAPClient initWith:channelMock];!! [client sendMessage:msgMock];
! [msgMock verify];! [channelMock verify];}
Verify
What’s wrong with Unit Testing?
TEST
Tdd isn’t about tests, but about behaviors and specifications
A spoonful of syntactic sugar...
A spoonful of syntactic sugar...
Behavior Driven Development
should insted of testmatchers instead of Assert
Bdd framework for iPhone Dev...
Bdd framework for iPhone Dev...
-(void)before {! [SpecHelper loginAsAdmin];}
-(void)itShouldAddAUser {! [app.navigationButton touch];! [[app.textField.with placeholder:@"First Name"] setText:@"Brian"];! [[app.textField.with placeholder:@"Last Name"] setText:@"Knorr"];! [[app.textField.with placeholder:@"Email"] setText:@"[email protected]"];! [[app.textField placeholder:@"Username"] setText:@"bkuser"];! [[app.textField placeholder:@"Password"] setText:@"test"];! [[app.textField placeholder:@"Confirm"] setText:@"test"];! [[app.navigationButton.label text:@"Save"] touch];
! [app timeout:1].alertView.should.not.exist;! [[app.tableView.label text:@"Brian Knorr"] should].exist;}
-(void)after {! [SpecHelper logout];}
Spec Example
-(void)before {! [SpecHelper loginAsAdmin];}
-(void)itShouldAddAUser {! [app.navigationButton touch];! [[app.textField.with placeholder:@"First Name"] setText:@"Brian"];! [[app.textField.with placeholder:@"Last Name"] setText:@"Knorr"];! [[app.textField.with placeholder:@"Email"] setText:@"[email protected]"];! [[app.textField placeholder:@"Username"] setText:@"bkuser"];! [[app.textField placeholder:@"Password"] setText:@"test"];! [[app.textField placeholder:@"Confirm"] setText:@"test"];! [[app.navigationButton.label text:@"Save"] touch];
! [app timeout:1].alertView.should.not.exist;! [[app.tableView.label text:@"Brian Knorr"] should].exist;}
-(void)after {! [SpecHelper logout];}
Fixture Setup
-(void)before {! [SpecHelper loginAsAdmin];}
-(void)itShouldAddAUser {! [app.navigationButton touch];! [[app.textField.with placeholder:@"First Name"] setText:@"Brian"];! [[app.textField.with placeholder:@"Last Name"] setText:@"Knorr"];! [[app.textField.with placeholder:@"Email"] setText:@"[email protected]"];! [[app.textField placeholder:@"Username"] setText:@"bkuser"];! [[app.textField placeholder:@"Password"] setText:@"test"];! [[app.textField placeholder:@"Confirm"] setText:@"test"];! [[app.navigationButton.label text:@"Save"] touch];
! [app timeout:1].alertView.should.not.exist;! [[app.tableView.label text:@"Brian Knorr"] should].exist;}
-(void)after {! [SpecHelper logout];}
Exercise SUT
-(void)before {! [SpecHelper loginAsAdmin];}
-(void)itShouldAddAUser {! [app.navigationButton touch];! [[app.textField.with placeholder:@"First Name"] setText:@"Brian"];! [[app.textField.with placeholder:@"Last Name"] setText:@"Knorr"];! [[app.textField.with placeholder:@"Email"] setText:@"[email protected]"];! [[app.textField placeholder:@"Username"] setText:@"bkuser"];! [[app.textField placeholder:@"Password"] setText:@"test"];! [[app.textField placeholder:@"Confirm"] setText:@"test"];! [[app.navigationButton.label text:@"Save"] touch];
! [app timeout:1].alertView.should.not.exist;! [[app.tableView.label text:@"Brian Knorr"] should].exist;}
-(void)after {! [SpecHelper logout];}
Verify Result
-(void)before {! [SpecHelper loginAsAdmin];}
-(void)itShouldAddAUser {! [app.navigationButton touch];! [[app.textField.with placeholder:@"First Name"] setText:@"Brian"];! [[app.textField.with placeholder:@"Last Name"] setText:@"Knorr"];! [[app.textField.with placeholder:@"Email"] setText:@"[email protected]"];! [[app.textField placeholder:@"Username"] setText:@"bkuser"];! [[app.textField placeholder:@"Password"] setText:@"test"];! [[app.textField placeholder:@"Confirm"] setText:@"test"];! [[app.navigationButton.label text:@"Save"] touch];
! [app timeout:1].alertView.should.not.exist;! [[app.tableView.label text:@"Brian Knorr"] should].exist;}
-(void)after {! [SpecHelper logout];}
Fixture Teardown
Run On Simulator
No XCode Integration Yet
Conclusions
The Market Asks For Apps
More Apps...
More Apps...
More Apps!
Lot to Improve
Be Good Kids:
Test First!
Reclame
http://milano-xpug.pbworks.com/
http://tech.groups.yahoo.com/group/milano-xpug/
Questions?