Ios 设计一个可测试的Objective-C类
背景 我主要使用Java。下面是我用Objective-C编写的一些代码。我的目标是为“MyService”编写一些单元测试。仅供参考:此代码中的类名称只是一个占位符 问题Ios 设计一个可测试的Objective-C类,ios,objective-c,unit-testing,testing,mocking,Ios,Objective C,Unit Testing,Testing,Mocking,背景 我主要使用Java。下面是我用Objective-C编写的一些代码。我的目标是为“MyService”编写一些单元测试。仅供参考:此代码中的类名称只是一个占位符 问题 这似乎是一个合理的方法吗?我是: 给这个类一个单例方法(sharedMyService) 声明要注入的构造函数NSURLSession 合并使用realNSURLSession 我的意图是: 我想通过模拟NSURLSession 我将把NSURLSessionmock注入这个类来测试它 可能会阅读更多关于Apple文档
这似乎是一个合理的方法吗?我是:
- 给这个类一个单例方法(
)sharedMyService
- 声明要注入的构造函数
NSURLSession
- 合并使用real
NSURLSession
- 我想通过模拟
NSURLSession
- 我将把
mock注入这个类来测试它NSURLSession
- 可能会阅读更多关于Apple文档或其他地方的内容,了解如何使用URL:方法模拟
DataTask
#import <Foundation/Foundation.h>
@interface MyService : NSObject
@property(nonatomic, strong, readwrite) NSURLSession *session;
+ (id)sharedMyService;
- (instancetype)initWithURLSession:(NSURLSession *)session;
- (void)fetchData:(NSString*)location;
@end
“这似乎是一种合理的方法吗?”是的(尽管我们不知道您的测试旨在证明什么,也不知道您的其他代码将如何使用这个类)
显然还有其他选择,比如使用子类来实现额外的可测试性。这里的好处是,您不会因为将类公开声明为单例类和可实例化类而混淆用户
还要考虑一下单例是否真的是适合您的方法-您是否希望能够在不同的会话中重用此类-以及工厂方法是否更好…需要考虑以下几点:
- 在我看来,设计iOS代码(Objective-C或Swift)与用Java设计代码没有什么不同。同样的原则也适用,你也会这样做
- 尽可能避免单身。例如,在类上仍然可以有一个访问器来返回服务,但也可以有一个方法来清除、重置或注入新实例。纯单例模式是测试后端的一大难题,在许多iOS项目中,单例经常被过度使用。在我的代码中,尽管我可以编写单例代码,但我通常也会将它们设置为可设置的(即使只是从单元测试源代码中的一个类别)
- 研究Objective-C模拟框架。我已经用了很多年了,几乎没有什么不能做的。与Easymock和Mockito等Java框架非常相似。但是请注意,如果您正在考虑使用Swift,那么对Swift的嘲笑几乎是不存在的。Swift根本不具备运行时功能
#import "MyService.h"
@implementation MyService
static NSString *const targetUri = @"http://something.com/%@";
+ (instancetype)sharedMyService {
static MyService *service = nil;
@synchronized (self) {
if (service == nil) {
service = [[self alloc] init];
}
}
return service;
}
- (instancetype)initWithURLSession:(NSURLSession *)session {
self = [super init];
if (self) {
self.session = session;
}
return self;
}
- (instancetype)init {
return [self initWithURLSession:[NSURLSession sharedSession]];
}
- (NSURLSession *)getSession {
return self.session;
}
- (void)fetchData:(NSString*)location {
NSURL *targetUrl = [NSURL URLWithString:[NSString stringWithFormat:targetUri, location]];
NSURLSession *urlSession = [self getSession];
NSURLSessionTask *sessionTask = [urlSession dataTaskWithURL:targetUrl completionHandler: ^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"Request succeeded.");
}];
[sessionTask resume];
}