如何验证部分模拟是否具有使用ocmock的args调用的基本方法? [英] How to verify a partial mock has a base method invoked with args using ocmock?

查看:97
本文介绍了如何验证部分模拟是否具有使用ocmock的args调用的基本方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用一个非常简单的Web服务,该服务使用基类重用一些常用功能.被测试的主要方法只是构建一个url,然后使用带有此参数的super/base方法.

- (void)getPlacesForLocation:(Location *)location WithKeyword:(NSString *)keyword
{
    NSString *gps = [NSString stringWithFormat:@"?location=%@,%@", location.lat, location.lng];
    NSURL *url = [[NSURL alloc] initWithString:[NSString stringWithFormat:@"%@%@", self.baseurl, gps]];
    [super makeGetRequestWithURL:url];
}

这是基本方法定义

@implementation WebService
@synthesize responseData = _responseData;

- (id)init
{
    if (self == [super init])
    {
        self.responseData = [NSMutableData new];        
    }

    return self;
}

- (void)makeGetRequestWithURL:(NSURL *)url
{
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
    request.HTTPMethod = @"GET";

    [[NSURLConnection alloc] initWithRequest:request delegate:self];
}

在测试中,我创建了部分模拟,因为我仍然想调用被测对象,但是我需要能够验证是否以特定方式调用了super方法.

- (void)testGetRequestMadeWithUrl
{
    self.sut = [[SomeWebService alloc] init];
    Location *location = [[Location alloc] initWithLatitude:@"-33.8670522" AndLongitude:@"151.1957362"];
    NSURL *url = [[NSURL alloc] initWithString:[NSString stringWithFormat:@"%@%@", self.sut.baseurl, @"?location=-33.8670522,151.1957362"]];
    id mockWebService = [OCMockObject partialMockForObject: self.sut];
    [[mockWebService expect] makeGetRequestWithURL:url];
    [self.sut getPlacesForLocation:location WithKeyword:@"foo"];
    [mockWebService verify];
}

但是,当我运行此测试时,我失败并显示以下错误:

未调用预期方法:makeGetRequestWithURL:https://...

我可以告诉这个方法不是模拟方法,因为如果我将NSLog放入基本方法中,则在运行ocunit测试时它会显示出来(显然它正在运行,只是没有像我想的那样模拟它).

如何修改测试/重构实现代码以获取所需的断言?

解决方案

这是一个有趣的案例.我的假设是,如果将"super"替换为"self",那么一切都会按预期进行,即.

- (void)getPlacesForLocation:(Location *)location WithKeyword:(NSString *)keyword
{
    NSString *gps = [NSString stringWithFormat:@"?location=%@,%@", location.lat, location.lng];
    NSURL *url = [[NSURL alloc] initWithString:[NSString stringWithFormat:@"%@%@", self.baseurl, gps]];
    [self makeGetRequestWithURL:url];
}

问题在于,部分模拟是通过动态创建子类来实现的.使用"super"时,对方法的查找从父类(在您的情况下为基类)开始,这意味着运行时永远不会看到由部分模拟创建的子类中实现的方法.

您的问题的另一个答案是更改设计.而不是使用类层次结构,而是使用两个类.一类负责创建URL,另一类负责发出请求.然后,您将不需要部分模拟,因为您可以简单地替换请求生成器.参见单一责任原则[1]和继承之上的组成[2].

[1] http://en.wikipedia.org/wiki/Single_responsibility_principle

[2] http://en.wikipedia.org/wiki/Composition_over_inheritance

I'm working with a very simple web service that uses a base class to reuse some commonly used functionality. The main method under test simply builds a url and then it uses a super / base method with this argument.

- (void)getPlacesForLocation:(Location *)location WithKeyword:(NSString *)keyword
{
    NSString *gps = [NSString stringWithFormat:@"?location=%@,%@", location.lat, location.lng];
    NSURL *url = [[NSURL alloc] initWithString:[NSString stringWithFormat:@"%@%@", self.baseurl, gps]];
    [super makeGetRequestWithURL:url];
}

Here is the base method definition

@implementation WebService
@synthesize responseData = _responseData;

- (id)init
{
    if (self == [super init])
    {
        self.responseData = [NSMutableData new];        
    }

    return self;
}

- (void)makeGetRequestWithURL:(NSURL *)url
{
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
    request.HTTPMethod = @"GET";

    [[NSURLConnection alloc] initWithRequest:request delegate:self];
}

In my test I created a partial mock because I still want to call into my object under test, but I need the ability to verify the super method is invoked in a specific way.

- (void)testGetRequestMadeWithUrl
{
    self.sut = [[SomeWebService alloc] init];
    Location *location = [[Location alloc] initWithLatitude:@"-33.8670522" AndLongitude:@"151.1957362"];
    NSURL *url = [[NSURL alloc] initWithString:[NSString stringWithFormat:@"%@%@", self.sut.baseurl, @"?location=-33.8670522,151.1957362"]];
    id mockWebService = [OCMockObject partialMockForObject: self.sut];
    [[mockWebService expect] makeGetRequestWithURL:url];
    [self.sut getPlacesForLocation:location WithKeyword:@"foo"];
    [mockWebService verify];
}

Yet when I run this test I fail with the following error:

expected method was not invoked: makeGetRequestWithURL:https://...

I can tell this method isn't being mock because if I put an NSLog into the base method it shows up when I run the ocunit test (clearly it's running, just not mocking it as I would like).

How can I modify my test / refactor my implementation code to get the assertion I'm looking for?

解决方案

This is an interesting case. My assumption is that if you replace "super" with "self" then everything will work as expected, ie.

- (void)getPlacesForLocation:(Location *)location WithKeyword:(NSString *)keyword
{
    NSString *gps = [NSString stringWithFormat:@"?location=%@,%@", location.lat, location.lng];
    NSURL *url = [[NSURL alloc] initWithString:[NSString stringWithFormat:@"%@%@", self.baseurl, gps]];
    [self makeGetRequestWithURL:url];
}

The issue is that the partial mocks are implemented by creating subclasses on the fly. When using "super" the lookup for the method starts at the parent class, the base class in your case, which means the runtime never sees the method implemented in the subclass created by the partial mock.

A different answer to your question is to change the design. Rather than using a class hierarchy use two classes. One class would be responsible for creating the URL, the other for making the requests. Then you wouldn't need partial mocks because you could simply substitute the request maker. See Single Responsibility Principle [1] and Composition over Inheritance [2].

[1] http://en.wikipedia.org/wiki/Single_responsibility_principle

[2] http://en.wikipedia.org/wiki/Composition_over_inheritance

这篇关于如何验证部分模拟是否具有使用ocmock的args调用的基本方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆