How to verify a partial mock has a base method invoked with args using ocmock?

只谈情不闲聊 提交于 2019-12-11 07:48:22

问题


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?


回答1:


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



来源:https://stackoverflow.com/questions/9785949/how-to-verify-a-partial-mock-has-a-base-method-invoked-with-args-using-ocmock

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!