testing (eng)
Post on 14-Aug-2015
142 Views
Preview:
TRANSCRIPT
iOS TestingDerrick Chao @ Loopd
Frameworks
• XCTest
• OCMock
• KIF
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, ...);
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]); }
Test Async Function Example
• send a message to another user
• async
• block callback
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]; }
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; }
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); }
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"]); }
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
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); }
KIF• KIF iOS Integration Testing Framework
KIF Example@interface UITests : KIFTestCase
@end
@implementation UITests
- (void)testLoginSteps { [tester tapViewWithAccessibilityLabel:@"regular_button"]; [tester enterText:@"123@123.com" intoViewWithAccessibilityLabel:@"emailTextField"]; [tester enterText:@"abcdefg" intoViewWithAccessibilityLabel:@"passwordTextField"]; [tester tapViewWithAccessibilityLabel:@“checkmark_button"]; }
@end
MVC & MVP
MVC• view can retrieve data
from model directly
• view hosts all the UI controls
• high coupling
MVC & MVP
MVP• MVP is a derivation of the
MVC architectural pattern
• view can’t retrieve data from model directly
• low coupling
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
- (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; }
- (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; }
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];
}
Spike Solution
Spike Solution
• not sure how to achieve some requirement.
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.
Spike Solution (3)• open a new branch
• do some test in the branch
• Throw it away when you’re done with it
TDD with Xcode• requirement: a helper for add two
integers
TDD with Xcode (2)• create files
TDD with Xcode (3)
TDD with Xcode (4)• command+u
TDD with Xcode (5)• verify the function
TDD with Xcode (6)• finish the function then test again
how I feel for TDD at beginning
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
The End
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
top related