diff --git a/Source/OCMock/OCMock.h b/Source/OCMock/OCMock.h index 9d558135..807ca569 100644 --- a/Source/OCMock/OCMock.h +++ b/Source/OCMock/OCMock.h @@ -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] diff --git a/Source/OCMock/OCMockObject.h b/Source/OCMock/OCMockObject.h index 31f7ac41..afd502c8 100644 --- a/Source/OCMock/OCMockObject.h +++ b/Source/OCMock/OCMockObject.h @@ -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; diff --git a/Source/OCMock/OCMockObject.m b/Source/OCMock/OCMockObject.m index a4e0b268..04b4af6d 100644 --- a/Source/OCMock/OCMockObject.m +++ b/Source/OCMock/OCMockObject.m @@ -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]; @@ -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 { @@ -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 diff --git a/Source/OCMock/OCProtocolMockObject.h b/Source/OCMock/OCProtocolMockObject.h index 0b3f6b12..df6e7303 100644 --- a/Source/OCMock/OCProtocolMockObject.h +++ b/Source/OCMock/OCProtocolMockObject.h @@ -18,10 +18,11 @@ @interface OCProtocolMockObject : OCMockObject { - Protocol *mockedProtocol; + NSArray *mockedProtocols; } - (id)initWithProtocol:(Protocol *)aProtocol; +- (id)initWithProtocols:(NSArray *)protocols; @end diff --git a/Source/OCMock/OCProtocolMockObject.m b/Source/OCMock/OCProtocolMockObject.m index 55dc7b52..d32a36d3 100644 --- a/Source/OCMock/OCProtocolMockObject.m +++ b/Source/OCMock/OCProtocolMockObject.m @@ -26,14 +26,26 @@ - (id)initWithProtocol:(Protocol *)aProtocol { NSParameterAssert(aProtocol != nil); [super init]; - mockedProtocol = aProtocol; + mockedProtocols = @[aProtocol]; return self; } +- (id)initWithProtocols:(NSArray *)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 @@ -41,18 +53,28 @@ - (NSString *)description - (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 diff --git a/Source/OCMockTests/OCMockObjectProtocolMocksTests.m b/Source/OCMockTests/OCMockObjectProtocolMocksTests.m index 4fa44774..868408dc 100644 --- a/Source/OCMockTests/OCMockObjectProtocolMocksTests.m +++ b/Source/OCMockTests/OCMockObjectProtocolMocksTests.m @@ -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)]; @@ -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)]; @@ -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]); @@ -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 diff --git a/Source/OCMockTests/OCMockObjectRuntimeTests.m b/Source/OCMockTests/OCMockObjectRuntimeTests.m index abea650a..2b55cf11 100644 --- a/Source/OCMockTests/OCMockObjectRuntimeTests.m +++ b/Source/OCMockTests/OCMockObjectRuntimeTests.m @@ -122,6 +122,9 @@ - (void)testCanMockNSMutableArray { id mock = [OCMockObject niceMockForClass:[NSMutableArray class]]; id anArray = [[NSMutableArray alloc] init]; + + XCTAssertNotNil(mock); + XCTAssertNotNil(anArray); }