testing (eng)

39
iOS Testing Derrick Chao @ Loopd

Upload: derrick-chao

Post on 14-Aug-2015

140 views

Category:

Engineering


3 download

TRANSCRIPT

Page 1: Testing (eng)

iOS TestingDerrick Chao @ Loopd

Page 2: Testing (eng)

Frameworks

• XCTest

• OCMock

• KIF

Page 3: Testing (eng)

XCTest• XCTest is the testing framework in Xcode

• A test method is an instance method of a test class that begins with the prefix test- (void)testExample { // This is an example of a functional test case. XCTAssert(YES, @"Pass"); XCTAssert(1+1==2); XCTAssertTrue(1+1==2); XCTAssertFalse(1+1==3); }

• XCTAssert XCTAssert(expression, …); XCTAssertTrue(expression, …); XCTAssertFalse(expression, ...);

Page 4: Testing (eng)

Unit Test Example- (void)testValidatePassword { NSString *password1 = @"AbCd1234"; NSString *password2 = @"AbCd12"; NSString *password3 = @"ABCD"; NSString *password4 = @"AbCd1"; NSString *password5 = @""; XCTAssertTrue([Helper validatePassword:password1]); XCTAssertTrue([Helper validatePassword:password2]); XCTAssertFalse([Helper validatePassword:password3]); XCTAssertFalse([Helper validatePassword:password4]); XCTAssertFalse([Helper validatePassword:password5]); }

Page 5: Testing (eng)

Test Async Function Example

• send a message to another user

• async

• block callback

Page 6: Testing (eng)

Test Async Function Example

- (void)testSendMessageWithNilMessage { XCTestExpectation *expectation =

[self expectationWithDescription:@"completion should be called"]; [Message sendMessage:nil recipientInfo:nil completion:^(id responseObject, NSError *error) { [expectation fulfill]; XCTAssertNotNil(error); XCTAssert([error.userInfo[@"message"] isEqualToString:@"message can't be nil"]); }]; [self waitForExpectationsWithTimeout:2 handler:nil]; }

Page 7: Testing (eng)

Singleton// LoopdManager.h file + (instancetype)sharedManager;

// LoopdManager.m file + (instancetype)sharedManager { static dispatch_once_t onceToken; static LoopdManager *sharedInstance; dispatch_once(&onceToken, ^{ sharedInstance = [[LoopdManager alloc] init]; }); return sharedInstance; }

Page 8: Testing (eng)

Test Singleton Example

- (void)testSharedManager { LoopdManager *manager1 = [LoopdManager sharedManager]; LoopdManager *manager2 = [LoopdManager sharedManager]; LoopdManager *manager3 = [LoopdManager new]; XCTAssertNotNil(manager1); XCTAssertNotNil(manager2); XCTAssertEqual(manager1, manager2); XCTAssertNotEqual(manager1, manager3); }

Page 9: Testing (eng)

OCMock• mock objects are simulated objects that mimic the

behavior of real objects in controlled ways

- (void)testMock { id mockUserInfo = OCMClassMock([UserInfo class]); // fullname should be nil for now XCTAssertNil([mockUserInfo fullname]); // make a custom return value OCMStub([mockUserInfo fullname]).andReturn(@"David"); // fullname should be David now XCTAssert([[mockUserInfo fullname] isEqualToString:@"David"]); }

Page 10: Testing (eng)

OCMock Example (1)ListViewController has a collection view find all SomeModel from server when viewDidLoad

// in SomeModel.h file@interface SomeModel : ModelObject

+ (void)findAllByCurrentUserId:(NSString *)currentUserId completion:(ArrayResultBlock)completion;

@end

Page 11: Testing (eng)

OCMock Example (2)- (void)testCollectionViewCellNumber { id mockSomeModel = OCMClassMock([SomeModel class]); OCMStub([mockSomeModel findAllRelationshipsByCurrentUserId:[OCMArg any] completion:[OCMArg any]]).andDo(^(NSInvocation *invocation) { ArrayResultBlock completion; [invocation getArgument:&completion atIndex: 3]; NSArray *someModels = @[@“1”, @“2”, @“3”, @“4”, @“5”]; completion(someModels, nil); }); ListViewController *listVC = [ListViewController new]; listVC.view; NSInteger num = [listVC collectionView:nil numberOfItemsInSection:0]; XCTAssert(num == 5); XCTAssert(num != 6); }

Page 12: Testing (eng)

KIF• KIF iOS Integration Testing Framework

Page 13: Testing (eng)
Page 14: Testing (eng)

KIF Example@interface UITests : KIFTestCase

@end

@implementation UITests

- (void)testLoginSteps { [tester tapViewWithAccessibilityLabel:@"regular_button"]; [tester enterText:@"[email protected]" intoViewWithAccessibilityLabel:@"emailTextField"]; [tester enterText:@"abcdefg" intoViewWithAccessibilityLabel:@"passwordTextField"]; [tester tapViewWithAccessibilityLabel:@“checkmark_button"]; }

@end

Page 15: Testing (eng)

MVC & MVP

Page 16: Testing (eng)

MVC• view can retrieve data

from model directly

• view hosts all the UI controls

• high coupling

Page 17: Testing (eng)

MVC & MVP

Page 18: Testing (eng)

MVP• MVP is a derivation of the

MVC architectural pattern

• view can’t retrieve data from model directly

• low coupling

Page 19: Testing (eng)

MVP's advantage• easier to unit test, clean

separation of the View and Model

• maintainability of UI increases due to almost no dependency on business logic

• Usually view to presenter map one to one. Complex views may have multi presenters

Page 20: Testing (eng)

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath]; // config cell Car *car = self.cars[indexPath.item]; cell.makeLabel.text = car.make; cell.modelLabel.text = car.model;

cell.yearLabel.text = car.year; cell.detailLabel.text = car.detail;

return cell; }

Page 21: Testing (eng)

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath]; // config cell Car *car = self.cars[indexPath.item]; cell.car = car;

[cell showUI]; return cell; }

Page 22: Testing (eng)
Page 23: Testing (eng)

Test Cell- (void)testCollectionViewCellNumber { id mockSomeModel = OCMClassMock([SomeModel class]); OCMStub([mockSomeModel findAllRelationshipsByCurrentUserId:[OCMArg any] completion:[OCMArg any]]).andDo(^(NSInvocation *invocation) { ArrayResultBlock completion; [invocation getArgument:&completion atIndex: 3]; NSArray *someModels = @[mod1, mod2, mod3, mod4, mod5]; completion(someModels, nil); }); ListViewController *listVC = [ListViewController new]; listVC.view; NSInteger num = [listVC collectionView:nil numberOfItemsInSection:0]; XCTAssert(num == 5); XCTAssert(num != 6);

UICollectionViewCell *cell = [contactListVC collectionView:nil cellForItemAtIndexPath:0];

}

Page 24: Testing (eng)
Page 25: Testing (eng)
Page 26: Testing (eng)

Spike Solution

Page 27: Testing (eng)

Spike Solution

• not sure how to achieve some requirement.

Page 28: Testing (eng)

Spike Solution (2)• A spike solution is dirty and quick.

Code just enough to get the answer you need.

• Throw it away when you’re done with it.

Page 29: Testing (eng)

Spike Solution (3)• open a new branch

• do some test in the branch

• Throw it away when you’re done with it

Page 30: Testing (eng)

TDD with Xcode• requirement: a helper for add two

integers

Page 31: Testing (eng)

TDD with Xcode (2)• create files

Page 32: Testing (eng)

TDD with Xcode (3)

Page 33: Testing (eng)

TDD with Xcode (4)• command+u

Page 34: Testing (eng)

TDD with Xcode (5)• verify the function

Page 35: Testing (eng)

TDD with Xcode (6)• finish the function then test again

Page 36: Testing (eng)

how I feel for TDD at beginning

Page 37: Testing (eng)

benefits of Testing/TDD• test => spec

• saves you development time

• saves you development time

• Good unit testing forces good architecture.  In order to make your code unit-testable, it must be properly modularized, by writing the unit testing first various architectural problems tend to surface earlier

Page 38: Testing (eng)

The End

Page 39: Testing (eng)

references• http://iosunittesting.com

• http://stackoverflow.com/questions/1053406/why-does-apple-say-iphone-apps-use-mvc

• http://blog.sanc.idv.tw/2011/09/uimvp-passive-view.html