iOS Framework 單元測試(一)-- XCTests

JiandanDream發表於2018-04-18

寫在前面

單元測試的重要性,不言而喻,在迭代開發 Framework 的過程中,好的單元測試,能及早發現問題。

關於 iOS Framework 的單元測試,常見的工具,當屬官方提供的 XCTests,但它也有些侷限,所以筆者寫了個小工具,對其進行補充。

本文會簡單介紹 XCTests 的使用。

建立工程

新建一個 SDKDemo 工程,記得勾上 Include Unit Tests

createSDK.png

生成工程如圖,會有一個SDKDemoTests的 Target 和相應的目錄

SDKTests.png

新建 NetworkRequest,作為測試物件,程式碼如下:

@interface NetworkRequest : NSObject
- (BOOL)configure;
@end

@implementation NetworkRequest
- (BOOL)configure {
    return YES;
}
@end
複製程式碼

使用 XCTests 進行單元測試

在新建模板裡選擇 Unit Tests Case Class,建立 NetworkRequestTests 如圖:

createUnitTests.png
新增程式碼:

#import <XCTest/XCTest.h>
#import "NetworkRequest.h"

@interface NetworkRequestTests : XCTestCase
@property (nonatomic, strong) NetworkRequest *request;
@end

@implementation NetworkRequestTests

- (void)setUp {
    [super setUp];
    // Put setup code here. This method is called before the invocation of each test method in the class.
    self.request = [NetworkRequest new];
}

- (void)tearDown {
    // Put teardown code here. This method is called after the invocation of each test method in the class.
    [super tearDown];
}

- (void)testExample {
    // This is an example of a functional test case.
    // Use XCTAssert and related functions to verify your tests produce the correct results.
}

- (void)testPerformanceExample {
    // This is an example of a performance test case.
    [self measureBlock:^{
        // Put the code you want to measure the time of here.
    }];
}
// 新增的測試方法
- (void)testConfigure {
    XCTAssertTrue([self.request configure]);
}
@end
複製程式碼

對於每個 test 開頭的方法,Xcode 會在其左邊顯示一個圓點,點一下它,就可以執行這個測試方法,使用快捷鍵 command+u 會執行所有測試。

同時 XCTests 提供許多斷言,如上面使用的 XCTAssertTrue。

當斷言成立時,圓點會顯示為綠色,否則為紅色。

若有 Block,如何測試?

在 NetworkRequest 中新增程式碼:


@interface NetworkRequest : NSObject
- (void)loginWithCompletionHandler:(void (^)(BOOL success))handler;
@end

@implementation NetworkRequest
- (BOOL)configure {
    return YES;
}
- (void)loginWithCompletionHandler:(void (^)(BOOL))handler {
    NSLog(@"Begin login");
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        handler(YES);
    });
}
@end
複製程式碼

對於 Block 的測試,得使用 XCTestExpectation,並且在 Block 裡呼叫 fullfill 方法。否則測試不會等到 Block 執行,就直接退出。

測試程式碼:

@implementation NetworkRequestTests
- (void)testLogin {
    // 宣告一個 XCTestExpectation 物件
    XCTestExpectation *expectation = [[XCTestExpectation alloc] initWithDescription:@"login expectation"];
    [self.request loginWithCompletionHandler:^(BOOL success) {
        XCTAssertTrue(success);
        // 呼叫 fullfill,告知 XCTestExpectation 已出現
        [expectation fulfill];
    }];
    // 等待 XCTestExpectation
    [self waitForExpectationsWithTimeout:5 handler:nil];
}
@end
複製程式碼

程式碼覆蓋率

預設沒有開啟,可以編輯 Test Scheme,如圖,勾選該功能

勾選 code coverage

再次執行測試,然後如圖,檢視結果

codeCoverageResult.png

如何在單元測試中引用 Framework?

在開發 Framework 過程中,典型場景就是 A 引用了 B,對 A 進行測試時,正如下文所說的

Because unit test targets are missing the Linked Frameworks and Libraries section in their General settings tab, you must instead drag the built frameworks to the Link Binaries With Libraries build phase.

這種情況下,無法進行測試,這也是筆者一開始想要對 XCTests 進行補充的初衷。

但後來發現

In the Test target under the Build Settings tab, add @loader_path/Frameworks to the Runpath Search Paths if it isn't already present.

還是有解決方案的。

擴充套件

XCTests 作為官方提供的測試方法,上手簡單,十分適合用來做單元測試。

美中不足的是,XCTests 不支援真機測試,所以有些功能無法測試,也無法在真機上測試效能。

這種情況下,又該如何處理,請看《iOS Framework 單元測試(二)-- JDAppTests(XCTests的補充)》

參考資料

Apple Developer

Carthage

相關文章