问题
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