Skip to content

Add support for multiple protocol mocks #294

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Source/OCMock/OCMock.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,12 @@

#define OCMProtocolMock(protocol) [OCMockObject niceMockForProtocol:protocol]

#define OCMProtocolsMock(protocol, ...) [OCMockObject niceMockForProtocols:protocol, ## __VA_ARGS__]

#define OCMStrictProtocolMock(protocol) [OCMockObject mockForProtocol:protocol]

#define OCMStrictProtocolsMock(protocol, ...) [OCMockObject mockForProtocol:protocol, ## __VA_ARGS__]

#define OCMPartialMock(obj) [OCMockObject partialMockForObject:obj]

#define OCMObserverMock() [OCMockObject observerMock]
Expand Down
2 changes: 2 additions & 0 deletions Source/OCMock/OCMockObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,12 @@

+ (id)mockForClass:(Class)aClass;
+ (id)mockForProtocol:(Protocol *)aProtocol;
+ (id)mockForProtocols:(Protocol *)aProtocol, ...;
+ (id)partialMockForObject:(NSObject *)anObject;

+ (id)niceMockForClass:(Class)aClass;
+ (id)niceMockForProtocol:(Protocol *)aProtocol;
+ (id)niceMockForProtocols:(Protocol *)aProtocol, ...;

+ (id)observerMock;

Expand Down
38 changes: 38 additions & 0 deletions Source/OCMock/OCMockObject.m
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,15 @@ + (id)mockForProtocol:(Protocol *)aProtocol
return [[[OCProtocolMockObject alloc] initWithProtocol:aProtocol] autorelease];
}

+ (id)mockForProtocols:(Protocol *)aProtocol, ...
{
va_list args;
va_start(args, aProtocol);
id mock = [[[OCProtocolMockObject alloc] initWithProtocols:[self _variadicArgumentsToArray:aProtocol args:&args]] autorelease];
va_end(args);
return mock;
}

+ (id)partialMockForObject:(NSObject *)anObject
{
return [[[OCPartialMockObject alloc] initWithObject:anObject] autorelease];
Expand All @@ -70,6 +79,15 @@ + (id)niceMockForProtocol:(Protocol *)aProtocol
return [self _makeNice:[self mockForProtocol:aProtocol]];
}

+ (id)niceMockForProtocols:(Protocol *)aProtocol, ...
{
va_list args;
va_start(args, aProtocol);
id mock = [self _makeNice:[[[OCProtocolMockObject alloc] initWithProtocols:[self _variadicArgumentsToArray:aProtocol args:&args]] autorelease]];
va_end(args);
return mock;
}


+ (id)_makeNice:(OCMockObject *)mock
{
Expand Down Expand Up @@ -433,5 +451,25 @@ - (NSString *)_stubDescriptions:(BOOL)onlyExpectations
return outputString;
}

+ (NSArray *)_variadicArgumentsToArray:(id)firstObject args:(va_list *)args
{
NSMutableArray *variadicObjects = nil;

if(firstObject)
{
variadicObjects = [[[NSMutableArray alloc] init] autorelease];
[variadicObjects addObject:firstObject];

if(args)
{
id eachObject;
while ((eachObject = va_arg(*args, typeof(firstObject)))) {
[variadicObjects addObject:eachObject];
}
}
}

return variadicObjects;
}

@end
3 changes: 2 additions & 1 deletion Source/OCMock/OCProtocolMockObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@

@interface OCProtocolMockObject : OCMockObject
{
Protocol *mockedProtocol;
NSArray<Protocol *> *mockedProtocols;
}

- (id)initWithProtocol:(Protocol *)aProtocol;
- (id)initWithProtocols:(NSArray<Protocol *> *)protocols;

@end

38 changes: 30 additions & 8 deletions Source/OCMock/OCProtocolMockObject.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,33 +26,55 @@ - (id)initWithProtocol:(Protocol *)aProtocol
{
NSParameterAssert(aProtocol != nil);
[super init];
mockedProtocol = aProtocol;
mockedProtocols = @[aProtocol];
return self;
}

- (id)initWithProtocols:(NSArray<Protocol *> *)protocols
{
NSParameterAssert(protocols != nil);
[super init];
mockedProtocols = protocols;
return self;
}

- (NSString *)description
{
const char* name = protocol_getName(mockedProtocol);
return [NSString stringWithFormat:@"OCMockObject(%s)", name];
char* names = (char*)protocol_getName(mockedProtocols[0]);
for(NSUInteger ix = 1; ix < mockedProtocols.count; ix++)
{
asprintf(&names, "%s, %s", names, protocol_getName(mockedProtocols[ix]));
}
return [NSString stringWithFormat:@"OCMockObject(%s)", names];
}

#pragma mark Proxy API

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
struct { BOOL isRequired; BOOL isInstance; } opts[4] = { {YES, YES}, {NO, YES}, {YES, NO}, {NO, NO} };
for(int i = 0; i < 4; i++)
for(Protocol *aProtocol in mockedProtocols)
{
struct objc_method_description methodDescription = protocol_getMethodDescription(mockedProtocol, aSelector, opts[i].isRequired, opts[i].isInstance);
if(methodDescription.name != NULL)
return [NSMethodSignature signatureWithObjCTypes:methodDescription.types];
for(int i = 0; i < 4; i++)
{
struct objc_method_description methodDescription = protocol_getMethodDescription(aProtocol, aSelector, opts[i].isRequired, opts[i].isInstance);
if(methodDescription.name != NULL)
return [NSMethodSignature signatureWithObjCTypes:methodDescription.types];
}
}
return nil;
}

- (BOOL)conformsToProtocol:(Protocol *)aProtocol
{
return protocol_conformsToProtocol(mockedProtocol, aProtocol);
BOOL conformsToProtocol = NO;
for(Protocol *procotol in mockedProtocols)
{
conformsToProtocol = protocol_conformsToProtocol(procotol, aProtocol);
if(conformsToProtocol)
break;
}
return conformsToProtocol;
}

- (BOOL)respondsToSelector:(SEL)selector
Expand Down
47 changes: 44 additions & 3 deletions Source/OCMockTests/OCMockObjectProtocolMocksTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,30 @@ - (void)testCanMockFormalProtocol
[mock verify];
}

- (void)testCanMockMultipleFormalProtocols
{
id mock = [OCMockObject mockForProtocols:@protocol(NSLocking), @protocol(NSObject)];
[[mock expect] lock];
[[mock expect] isProxy];

[mock lock];
[mock isProxy];

[mock verify];
}

- (void)testSetsCorrectNameForProtocolMockObjects
{
id mock = [OCMockObject mockForProtocol:@protocol(NSLocking)];
XCTAssertEqualObjects(@"OCMockObject(NSLocking)", [mock description], @"Should have returned correct description.");
}

- (void)testSetsCorrectNameForMultiProtocolMockObjects
{
id mock = [OCMockObject mockForProtocols:@protocol(NSLocking), @protocol(NSObject)];
XCTAssertEqualObjects(@"OCMockObject(NSLocking, NSObject)", [mock description], @"Should have returned correct description.");
}

- (void)testRaisesWhenUnknownMethodIsCalledOnProtocol
{
id mock = [OCMockObject mockForProtocol:@protocol(NSLocking)];
Expand All @@ -88,6 +106,13 @@ - (void)testConformsToMockedProtocol
XCTAssertTrue([mock conformsToProtocol:@protocol(NSLocking)]);
}

- (void)testConformsToMockedMultiProtocols
{
id mock = [OCMockObject mockForProtocols:@protocol(NSLocking), @protocol(NSObject)];
XCTAssertTrue([mock conformsToProtocol:@protocol(NSLocking)]);
XCTAssertTrue([mock conformsToProtocol:@protocol(NSObject)]);
}

- (void)testRespondsToValidProtocolRequiredSelector
{
id mock = [OCMockObject mockForProtocol:@protocol(TestProtocol)];
Expand All @@ -106,19 +131,22 @@ - (void)testDoesNotRespondToInvalidProtocolSelector
XCTAssertFalse([mock respondsToSelector:@selector(testDoesNotRespondToInvalidProtocolSelector)]);
}

- (void)testWithTypedefReturnType {
- (void)testWithTypedefReturnType
{
id mock = [OCMockObject mockForProtocol:@protocol(ProtocolWithTypedefs)];
XCTAssertNoThrow([[[mock stub] andReturn:[TypedefInterface new]] typedefReturnValue1], @"Should accept a typedefed return-type");
XCTAssertNoThrow([mock typedefReturnValue1]);
}

- (void)testWithTypedefPointerReturnType {
- (void)testWithTypedefPointerReturnType
{
id mock = [OCMockObject mockForProtocol:@protocol(ProtocolWithTypedefs)];
XCTAssertNoThrow([[[mock stub] andReturn:[TypedefInterface new]] typedefReturnValue2], @"Should accept a typedefed return-type");
XCTAssertNoThrow([mock typedefReturnValue2]);
}

- (void)testWithTypedefParameter {
- (void)testWithTypedefParameter
{
id mock = [OCMockObject mockForProtocol:@protocol(ProtocolWithTypedefs)];
XCTAssertNoThrow([[mock stub] typedefParameter:nil], @"Should accept a typedefed parameter-type");
XCTAssertNoThrow([mock typedefParameter:nil]);
Expand Down Expand Up @@ -147,9 +175,22 @@ - (void)testProtocolClassMethod
XCTAssertEqual(@"stubbed", result, @"Should have stubbed the class method.");
}

- (void)testMultipleProtocolClassMethod
{
id mock = OCMProtocolsMock(@protocol(TestProtocol), @protocol(NSLocking));
OCMStub([mock stringValueClassMethod]).andReturn(@"stubbed");
id result = [mock stringValueClassMethod];
XCTAssertEqual(@"stubbed", result, @"Should have stubbed the class method.");
}

- (void)testRefusesToCreateProtocolMockForNilProtocol
{
XCTAssertThrows(OCMProtocolMock(nil));
}

- (void)testRefusesToCreateMultipleProtocolMockForNilProtocol
{
XCTAssertThrows(OCMProtocolsMock(nil));
}

@end
3 changes: 3 additions & 0 deletions Source/OCMockTests/OCMockObjectRuntimeTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@ - (void)testCanMockNSMutableArray
{
id mock = [OCMockObject niceMockForClass:[NSMutableArray class]];
id anArray = [[NSMutableArray alloc] init];

XCTAssertNotNil(mock);
XCTAssertNotNil(anArray);
}


Expand Down