關於iOS TDD&BDD的學習與使用
阿新 • • 發佈:2019-01-04
TDD(測試驅動開發 Test Driven Development),相比傳統的開發流程,會先寫測試,待測試通過再實際開發功能模組。這樣的好處是在你使用這些已經測試過的功能時,不用擔心他們的可用性。
BDD(行為驅動開發 Behavior Driven Development),相比TDD,相關測試程式碼更加集中,也更加簡單易懂
相關連結:
TDD的iOS開發初步以及Kiwi使用入門
http://onevcat.com/2014/02/ios-test-with-kiwi/
Kiwi 使用進階 Mock, Stub, 引數捕獲和非同步測試
http://onevcat.com/2014/05/kiwi-mock-stub-test/
PS:本文中主要介紹的是ReactiveCocoa的測試方法
使用OCUnit Test
1.模型測試
- (void)testPersonModel
{
NSString *string = @"{\"uname\":\"jack\",\"pic\":\"http://krplus-cdn.b0.upaiyun.com/common-module/common-header/images/logo.png\ "}";
Person *a = [Person data:[string jsonDictionary]];
XCTAssertNotNil(a, @"model is nil");
XCTAssertNotNil(a.name, @"person has not a name!");
XCTAssertNotNil(a.avatar, }
2.介面測試。
- (void)testJsonAPI
{
// 使用 measureBlock 方法可以記錄該測試的耗時
[self measureBlock:^{
NSString *urlStr = @"http://xiaoliao.sinaapp.com/index.php/Api369/index/pad/0/sw/1/cid/mm/lastTime/1441598591";
NSString *newUrlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:newUrlStr];
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10];
NSURLResponse *response = nil;
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSString *aString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
XCTAssertNotNil(aString, @"json is nil");
NSArray *jsonArray = [aString jsonArray];
XCTAssertNotNil(jsonArray, @"jsonArray is empty!");
[jsonArray enumerateObjectsUsingBlock:^(NSDictionary *json, NSUInteger idx, BOOL *stop) {
Person *a = [Person data:json];
XCTAssertNotNil(a, @"model is nil");
XCTAssertNotNil(a.name, @"person has not a name!");
XCTAssertNotNil(a.avatar, @"person has not a avatar!");
}];
}];
}
使用 kiwi框架
1.ViewModel的測試
SPEC_BEGIN(GinLoginViewModelSpec)
describe(@"GinLoginViewModel", ^{
__block GinLoginViewModel *loginVM;
//在scope內的每個it之前呼叫一次,對於context的配置程式碼應該寫在這裡
beforeEach(^{
loginVM = [[GinLoginViewModel alloc] init];
});
//在scope內的每個it之後呼叫一次,用於清理測試後的程式碼
afterEach(^{
loginVM = nil;
});
context(@"when username is 123456 and password is 123456", ^{
__block BOOL result = NO;
it(@"should return signal that value is NO", ^{
loginVM.username = @"123456";
loginVM.password = @"123456";
[[loginVM isValidUsernameAndPasswordSignal] subscribeNext:^(NSNumber *isValid) { result = [isValid boolValue];
}];
[[theValue(result) should] beNo];
});
});
context(@"when username is 15558066089 and password is 123456", ^{
__block BOOL result = NO;
it(@"should return signal that value is YES", ^{
loginVM.username = @"15558066089";
loginVM.password = @"123456";
[[loginVM isValidUsernameAndPasswordSignal] subscribeNext:^(NSNumber *isValid)
{
result = [isValid boolValue];
}];
[[theValue(result) should] beYes];
});
});
});
SPEC_END
2.ViewController測試
SPEC_BEGIN(GinLoginViewControllerSpec)
describe(@"GinLoginViewController", ^{
__block GinLoginViewController *loginVC;
__block NSString *username = @"15558066089";
__block NSString *password = @"abc123456";
beforeEach(^{
loginVC = [[GinLoginViewController alloc] init];
[loginVC view];
});
afterEach(^{
loginVC = nil;
});
context(@"bind ViewModel", ^{
it(@"textfiled bind success", ^{
loginVC.username.text = username;
loginVC.password.text = password;
[loginVC.username sendActionsForControlEvents:UIControlEventEditingChanged];
[loginVC.password sendActionsForControlEvents:UIControlEventEditingChanged];
[[loginVC.loginVM.username should] equal:loginVC.username.text];
[[loginVC.loginVM.password should] equal:loginVC.password.text];
});
it(@"command bind success", ^{
[[loginVC.nextStep.rac_command should] equal:loginVC.loginVM.loginCommand];
});
});
});
SPEC_END