Mar*_*k W 11 objective-c ocmock ios objective-c-blocks
这是测试中的方法:
- (void)loginWithUser:(NSString *)userName andPass:(NSString *)pass {
NSDictionary *userPassD = @{@"user":userName,
@"pass":pass};
[_loginCntrl loginWithUserPass:userPassD withSuccess:^(NSString *authToken){
// save authToken to credential store
} failure:^(NSString *errorMessage) {
// alert user pass was wrong
}];
}
Run Code Online (Sandbox Code Playgroud)
我要测试的是,在该成功块中,使用适当的方法调用其他依赖项/ OCMockObject _credStore.所以目前loginCtrl和credStore依赖是OCMockObjects,我可以存根/期望这些.
在调用时,我会将loginController以某种方式执行该块吗?我已经看过一些关于使用OCMock来阻塞块的问题,我无法理解他们正在做的事情以及是否适合这种情况.
实际上我想做的就是OCMock来激活块([成功调用] ??),这样代码_credStore saveUserPass就完成了,可以在_credStore上验证.
我停在哪里:
- (void)test_loginWithuserPass_succeeds_should_call_credStore_setAuthToken {
NSDictionary *userPassD = @{@"user":@"mark",
@"pass":@"test"};
id successBlock = ^ {
// ??? isn't this done in the SUT?
};
[[[_loginController stub] andDo:successBlock] loginWithUserPass:userPassD withSuccess:OCMOCK_ANY failure:OCMOCK_ANY];
[[_credentialStore expect] setAuthToken:@"passed back value from block"];
[_docServiceSUT loginWithUser:@"mark" andPass:@"test"];
[_credentialStore verify];
}
Run Code Online (Sandbox Code Playgroud)
ETA:这是我基于Ben的下面的示例,但没有工作,获得EXC_BAD_ACCESS异常:
// OCUnit test method
- (void)test_loginWithUserPass_success_block_should_call_credentials_setAuthToken {
void (^proxyBlock)(NSInvocation*) = ^(NSInvocation *invocation) {
void(^successBlock)(NSString *authToken);
[invocation getArgument:&successBlock atIndex:3]; // should be 3 because my block is the second param
successBlock(@"myAuthToken");
};
[[[_loginController expect] andDo:proxyBlock] loginWithUserPass:OCMOCK_ANY withSuccess:OCMOCK_ANY failure:OCMOCK_ANY];
[[_credentialStore expect] setAuthToken:@"myAuthToken"];
[_docServiceSUT loginWithUser:@"mark" andPass:@"myPass"];
[_loginController verify];
[_credentialStore verify];
}
//method under test
- (void)loginWithUser:(NSString *)userName andPass:(NSString *)pass {
NSDictionary *userPassD = @{@"user":userName,
@"pass":pass};
void(^onSuccess)(NSString *) = ^(NSString *authToken){
[SVProgressHUD dismiss];
[_credentials setAuthToken:authToken];
// Ask user to enter the 6 digit authenticator key
[self askUserForAuthenticatorKey];
};
void(^onFailure)(NSString *) = ^(NSString *errorMessage) {
[SVProgressHUD dismiss];
[_alertSender sendAlertWithMessage:errorMessage andTitle:@"Login failed"];
};
[SVProgressHUD show];
[_loginCntrl loginWithUserPass:userPassD withSuccess:onSuccess
failure:onFailure];
}
Run Code Online (Sandbox Code Playgroud)
Ben*_*ynn 13
如果我跟着你,这可能会做你想要的:
@interface ExampleLC : NSObject
- (void)loginWithUserPass:userPassD withSuccess:(void (^)(NSString *authToken))successBlock failure:(void (^)(NSString *errorMessage))failureBlock;
@end
@implementation ExampleLC
- (void)loginWithUserPass:userPassD withSuccess:(void (^)(NSString *authToken))successBlock failure:(void (^)(NSString *errorMessage))failureBlock
{
}
@end
@interface Example : NSObject {
@public
ExampleLC *_loginCntrl;
}
- (void)saveToken:(NSString *)authToken;
- (void)loginWithUser:(NSString *)userName andPass:(NSString *)pass;
@end
@implementation Example
- (void)saveToken:(NSString *)authToken
{
}
- (void)loginWithUser:(NSString *)userName andPass:(NSString *)pass {
NSDictionary *userPassD = @{@"user":userName,
@"pass":pass};
[_loginCntrl loginWithUserPass:userPassD withSuccess:^(NSString *authToken){
// save authToken to credential store
[self saveToken:authToken];
} failure:^(NSString *errorMessage) {
// alert user pass was wrong
}];
}
@end
@interface loginTest : SenTestCase
@end
@implementation loginTest
- (void)testExample
{
Example *exampleOrig = [[Example alloc] init];
id loginCntrl = [OCMockObject mockForClass:[ExampleLC class]];
[[[loginCntrl expect] andDo:^(NSInvocation *invocation) {
void (^successBlock)(NSString *authToken) = [invocation getArgumentAtIndexAsObject:3];
successBlock(@"Dummy");
}] loginWithUserPass:OCMOCK_ANY withSuccess:OCMOCK_ANY failure:OCMOCK_ANY];
exampleOrig->_loginCntrl = loginCntrl;
id example = [OCMockObject partialMockForObject:exampleOrig];
[[example expect] saveToken:@"Dummy"];
[example loginWithUser:@"ABC" andPass:@"DEF"];
[loginCntrl verify];
[example verify];
}
@end
Run Code Online (Sandbox Code Playgroud)
此代码允许强制使用您指定的参数调用实际成功块,然后您可以验证该参数.
我正在使用的代码很大程度上基于块,所以我对你的问题非常熟悉.
只是为了重新解释一下问题:
- (ReturnObject *)methodThatWeWantToTest:(Foobar *)bar
{
[self.somethingElse doSomethingAndCallBlock:^{
// really want to test this code
} secondBock:^{
// even more code to test
}];
}
Run Code Online (Sandbox Code Playgroud)
为了解决你在这里提出的完全相同的问题,我们创建了单元测试助手类,它具有与调用我们需要测试的块的方法具有相同签名的方法.
对于代码示例,它是一个不返回任何内容的模拟方法,需要一个id参数和两个块.
下面是您需要的模拟方法示例以及使用的OCMock单元测试示例
- (void)mockSuccessBlockWithOneArgumentTwoBlocks:(id)firstArgument
successBlock:(void (^)(NSString *authtoken))successBlock
failureBlock:(void (^)(NSString *errorMessage))failureBlock
{
successBlock(@"mocked unit test auth token");
}
- (void)testLoginWithUserCallsSomethingInCredStoreOnLoginSuccess
{
LoginService *loginService = [[LoginService alloc] init];
// init mocks we need for this test
id credStoreMock = [OCMockObject niceMockForClass:[MyCredStore class]];
id loginCtrlMock = [OCMockObject niceMockForClass:[MyLoginCtrl class]];
// force login controller to call success block when called with loginWithUserPass
// onObject:self - if mock method is in the same unit test, use self. if it is helper object, point to the helper object.
[[[loginCtrlMock stub] andCall:@selector(mockSuccessBlockWithOneArgumentTwoBlocks:successBlock:failureBlock::) onObject:self] loginWithUserPass:OCMOCK_ANY withSuccess:OCMOCK_ANY failure:OCMOCK_ANY];
// setup mocks
loginService.credStore = credStoreMock;
loginService.loginCtrl = loginCtrlMock;
// expect/run/verify
[[credStore expect] callSomethingFromSuccessBlock];
[loginService loginWithUser:@"testUser" andPass:@"testPass"];
[credStore verify];
}
Run Code Online (Sandbox Code Playgroud)
希望能帮助到你!如果您有任何问题,请告诉我们,我们已经开始工作了.
| 归档时间: |
|
| 查看次数: |
6335 次 |
| 最近记录: |