Skip to content

Commit 3296188

Browse files
committed
Add content
1 parent 6fb4dc9 commit 3296188

File tree

1 file changed

+82
-0
lines changed

1 file changed

+82
-0
lines changed

chapter6/testing_viewModels.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,89 @@ describe(@"FRPGalleryViewModel",^{
272272

273273
为了测试一个方法,测试代码也太多了吧! 我知道,我知道~ 这是OCMock没落的原因之一,它竟然需要这么多的模板。但你不能责怪它,因为它要工作在令它不寒而栗的Objective-C平台上!
274274

275+
我们创建了一个`FRPGalleryViewModel`的mock版本,告诉它期望`importPhotoSignal`被调用。然后才进行对象的初始化。这里使用了一点点技巧,因为我们在mockObject上调用了init方法,但它(init)实际上是一个NSProxy的子类。然后,对OCMock来讲,它足够聪明,它了解这一切,有能力做出正确的选择。只是看起来有点诡异罢了。我们使用`[mockObject init]``mockObject`赋值,也是为了屏蔽编译警告。最后我们验证了所有预期可能被调用的方法。
275276

277+
这个例子中表现出来的测试很困难的情况也说明了另一个问题,你应该避免视图模型的初始化方法产生"副作用"(参见前面章节提到的“函数的副作用”),应该使用`didBecomeActiveSignal`来代理。
278+
279+
下面我们来测试`FRPPhotoViewModel`.再次突出引起函数副作用和使用`didBecomeActiveSignal`的区别。
280+
281+
快速浏览下实现:
282+
283+
```Objective-C
284+
285+
@implementation FRPPhotoViewModel
286+
287+
- (intancetype)initWithModel:(FRPPhotoModel *)photoModel {
288+
self = [super initWithModel:photoModel];
289+
if(!self) return nil;
290+
291+
@weakify(self);
292+
[self.didBecomeActiveSignal subscribeNext:^ (id x) {
293+
@strongify(self);
294+
self.loading = YES;
295+
[[FRPPhotoImporter fetchPhotoDetails:self.model]
296+
subscribeError: ^ (NSError *error) {
297+
NSLog(@"Could not fetch photo details: %@",error);
298+
}
299+
completed: ^ {
300+
self.loading = NO;
301+
NSLog(@"Fetched photo details");
302+
}];
303+
}];
304+
305+
RAC(self, photoImage) = [RACObserve(self.model, fullsizedData) map:^id (id value) {
306+
return [UIImage imageWithData:value];
307+
}];
308+
309+
return self;
310+
}
311+
312+
- (NSString *)photoName {
313+
return self.model.photoName;
314+
}
315+
316+
@end
317+
318+
```
319+
首先我们来测试`photoName`方法:
320+
321+
```Objective-C
322+
#import <Specta/Specta.h>
323+
#define EXP_SHORTHAND
324+
#import <Expecta/Expecta.h>
325+
#import <OCMock/OCMock.h>
326+
327+
#import "FRPPhotoViewModel.h"
328+
#import "FRPPhotoModel.h"
329+
330+
SpecBegin(FRPPhotoViewModel)
331+
332+
describe (@"FRPPhotoViewModel", ^{
333+
it(@"should return the photo's name property when photoName is invoked", ^{
334+
NSString *name = @"Ash";
335+
336+
id mockPhotoModel = [OCMockObject mockForClass:[FRPPhotoModel class]];
337+
[[[mockPhotoModel stub] andReturn:name] photoName];
338+
339+
FRPPhotoViewModel *viewModel = [[FRPPhotoViewModel alloc] initWithModel:nil];
340+
id mockViewModel = [OCMockObject partialMockForObject:viewModel];
341+
[[[mockViewModel stub] andReturn:mockPhotoModel] model];
342+
343+
id returnName = [mockViewModel photoName];
344+
345+
expect(returnedName).to.equal(name);
346+
[mockPhotoModel stopMocking];
347+
});
348+
});
349+
350+
```
351+
我们为mock的视图模型的model属性添加了一个mockPhotoModel,它会mocks所有的途径。
352+
353+
现在来看这个复杂的初始化方法,这东西看起来真巨大!近20行纯粹的未经测试的代码。哎呀!让我们来一点点简化这个事情,并逐步加上我们的测试代码。
354+
355+
```Objective-C
356+
357+
```
276358

277359

278360

0 commit comments

Comments
 (0)