框架选择
eigen: Specta + OCMock + Expecta + OHHTTPStubs + FBSnapshotTestCase + "Expecta+Snapshots" + "XCTest+OHHTTPStubSuiteCleanUp”。
其次,參考公司内部别的项目使用情况。发现使用下面框架来做測试方面的事情: Specta + Expecta + OCMock + OHTTPStubs + KIF(UI Test)
so,我决定选择 Specta (BDD框架) + Expecta(断言框架) + OCMock(mock框架) + OHHTTPStubs(http stub框架) + KIF(UI Test) 做測试框架来学习。
XCTest简单介绍
因为我决定不直接使用XCTest作为測试框架。可是又因为Specta是基于XCTest进行封装的,所以对XCTest做一个基础的了解还是有必要的。
參考:
BDD框架 — Specta
1. 简单介绍
- An OC RSpec-like BDD DSL
- Quick and easy set up
- Build on top of XCTest
- Excellent Xcode integration
2. Specta BDD DSL语法简单介绍
1) SpecBegin 声明了一个測试类。SpecEnd 结束了类声明
2) describe (context) 块声明了一组实例
3) it (example/specify) 是一个单一的样例
4) beforeAll 是一个执行于全部同级块之前的块,仅仅执行一次。afterAll 与beforeAll相反,是在全部同级块之后执行的块。仅仅执行一次。
5) beforeEach/afterEach,在每一个同级块执行的时候,都会执行一次,而beforeAll/afterAll仅仅会执行一次
6) it/waitUntil/done()。异步调用,注意完毕异步操作之后。必须调用done()函数。例如以下:
7) sharedExamplesFor 和 itShouldBehaveLike结合在一起。能够实如今不同的spec之间共享同一套test case,參考:
http://artandlogic.com/2014/02/specta-shared-behavior/;sharedExamplesFor
设置多个spec之间共享的test case,第一个參数作为标识符。通过itShouldBehaveLike来执行spec中test case。第一个參数传入sharedExamplesFor设置时使用的标识符。注意。在describe局部使用sharedExamplesFor定义shared examples。能够在它作用域内覆盖全局的shared examples。
8) pending,仅仅打印一条log信息。不做測试。这个语句会给出一条警告,能够作为一開始集中书写行为描写叙述时还未实现的測试的提示。
断言框架 — Expecta
mock框架 —
OCMock
In a modern Object Oriented system, the component under test will likely have several object dependencies. Instead of instantiating dependencies as concrete classes, we use mocks. Mocks are ‘fake’ objects with pre-defined behavior to stand-in for concrete objects during testing. The component under test does not know the difference! With mocks, a component can be tested with confidence that it behaves as designed within a larger system.
eigen的一个test case,注意在运行完成的时候,须要调用stopMocking。OCMockObject是基于runtime方法转发实现的。mock一个对象,就是对这个对象的方法进行转发的过程,运行完成须要调用stopMocking,否则会影响其它test case的运行。
以下能够看出一个OCMock基本过程:获得OCMockObject -> stub方法 -> 设置expect ->
verify校验运行结果 -> 调用stopMocking
以下有一个mock一个alert view show的过程
參考:
OHHTTPStubs
[OHHTTPStubs
stubRequestsPassingTest:^
BOOL(
NSURLRequest *request) {
return [request.URL.host
isEqualToString:@"mywebservice.com"];
}
withStubResponse:^OHHTTPStubsResponse*(
NSURLRequest *request) {
// Stub it with our "wsresponse.json" stub file (which is in same bundle as self)
NSString* fixture =
OHPathForFile(
@"wsresponse.json", self.
class);
return [OHHTTPStubsResponse
responseWithFileAtPath:fixture
statusCode:200 headers:@{
@"Content-Type":
@"application/json"}];
}];
这个框架的主要用法就是上面这个演示样例,用法非常明显易用。结合unit test使用的时候。须要使用网络请求的时候。能够在it或者beforeAll或者beforeEach的时候进行stub request。即上面这段代码的行为。可是不要忘记的是。须要在tear down的时候,即specta的afterAll的时候,记得调用 [OHHTTPStubs removeAllStubs] 。
注意,这里仅仅是使用NSURLProtocol来stub request。不会影响被測试的请求接口的測试。请求是异步的话,能够使用Specta的it/waitUntil/done()流程对请求进行測试,假设使用XCTest的话,OHTTPStubs给出了一个wiki解决。使用XCTestExpectation来搞定。我认为挺有意思:
- (void)testFoo
{
NSURLRequest* request = ...
XCTestExpectation* responseArrived = [self expectationWithDescription:@"response of async request has arrived"];
__block NSData* receivedData = nil;
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse* response, NSData* data, NSError* error)
{
receivedData = data;
[responseArrived fulfill];
}
];
[self waitForExpectationsWithTimeout:timeout handler:^{
// By the time we reach this code, the while loop has exited
// so the response has arrived or the test has timed out
XCTAssertNotNil(receivedData, @"Received data should not be nil");
}];
}
因为NSURLProtocol的局限性。OHHTTPStubs没法用来測试background sessions和模拟数据上传。
F.I.R.S.T 原则
- Fast — 測试应该可以被常常执行
- Isolated — 測试本身不能依赖于外部因素或其它測试的结果
- Repeatable — 每次执行測试都应该产生同样的结果
- Self-verifying — 測试应该依赖于断言,不须要人为干预
- Timely — 測试应该和生产代码一同书写
怎样将測试结果收益最大化:不要将測试和实现细节耦合在一起。
- 不要測试私有方法
- 不要Stub私有方法
- 不要Stub外部库
- 正确地Stub依赖
- 不要測试构造函数
參考资料