Skip to content
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

Multiprotocols mocking and class+protocols combination mocking #309

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 14 additions & 0 deletions Source/OCMock.xcodeproj/project.pbxproj
Expand Up @@ -7,6 +7,10 @@
objects = {

/* Begin PBXBuildFile section */
012412861D5A0B31003B75B8 /* OCProtocolsProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 012412851D5A0B28003B75B8 /* OCProtocolsProxy.m */; };
012412871D5A0B32003B75B8 /* OCProtocolsProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 012412851D5A0B28003B75B8 /* OCProtocolsProxy.m */; };
012412881D5A0B32003B75B8 /* OCProtocolsProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 012412851D5A0B28003B75B8 /* OCProtocolsProxy.m */; };
012412891D5A0B32003B75B8 /* OCProtocolsProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 012412851D5A0B28003B75B8 /* OCProtocolsProxy.m */; };
030EF0B614632FD000B04273 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 030EF0B414632FD000B04273 /* InfoPlist.strings */; };
031E50581BB4A56300E257C3 /* OCMBoxedReturnValueProviderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 031E50571BB4A56300E257C3 /* OCMBoxedReturnValueProviderTests.m */; };
031E50591BB4A56300E257C3 /* OCMBoxedReturnValueProviderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 031E50571BB4A56300E257C3 /* OCMBoxedReturnValueProviderTests.m */; };
Expand Down Expand Up @@ -346,6 +350,9 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
012412841D5A0B28003B75B8 /* OCProtocolsProxy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OCProtocolsProxy.h; sourceTree = "<group>"; };
012412851D5A0B28003B75B8 /* OCProtocolsProxy.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OCProtocolsProxy.m; sourceTree = "<group>"; };
019DD2401D6F4753001055C3 /* TestProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TestProtocol.h; sourceTree = "<group>"; };
030EF0A814632FD000B04273 /* OCMock.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = OCMock.framework; sourceTree = BUILT_PRODUCTS_DIR; };
030EF0B314632FD000B04273 /* OCMock-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "OCMock-Info.plist"; sourceTree = "<group>"; };
030EF0B514632FD000B04273 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
Expand Down Expand Up @@ -598,6 +605,7 @@
03565A3318F0566F003AE91E /* OCMockTests */ = {
isa = PBXGroup;
children = (
019DD2401D6F4753001055C3 /* TestProtocol.h */,
2FA2822E19948FC997965267 /* OCMockObjectTests.m */,
03AC5C1416DF9FA500D82ECD /* OCMockObjectPartialMocksTests.m */,
039F91C516EFB493006C3D70 /* OCMockObjectClassMethodMockingTests.m */,
Expand Down Expand Up @@ -660,6 +668,8 @@
03B31619146334040052CD09 /* Invocation Actions */,
03B3161A146334320052CD09 /* Argument Constraints and Actions */,
037ECD5618FB0D2E00AF0E4C /* Helper */,
012412841D5A0B28003B75B8 /* OCProtocolsProxy.h */,
012412851D5A0B28003B75B8 /* OCProtocolsProxy.m */,
);
name = "Core Mocks";
sourceTree = "<group>";
Expand Down Expand Up @@ -1182,6 +1192,7 @@
03B315E3146333BF0052CD09 /* OCMNotificationPoster.m in Sources */,
03B315E8146333BF0052CD09 /* OCMObserverRecorder.m in Sources */,
03B315ED146333C00052CD09 /* OCMockObject.m in Sources */,
012412861D5A0B31003B75B8 /* OCProtocolsProxy.m in Sources */,
03B315F2146333C00052CD09 /* OCMStubRecorder.m in Sources */,
03B315F7146333C00052CD09 /* OCMPassByRefSetter.m in Sources */,
03B315FC146333C00052CD09 /* OCMRealObjectForwarder.m in Sources */,
Expand Down Expand Up @@ -1222,6 +1233,7 @@
03B315E0146333BF0052CD09 /* OCMIndirectReturnValueProvider.m in Sources */,
03B315E5146333BF0052CD09 /* OCMNotificationPoster.m in Sources */,
03B315EA146333BF0052CD09 /* OCMObserverRecorder.m in Sources */,
012412871D5A0B32003B75B8 /* OCProtocolsProxy.m in Sources */,
03B315EF146333C00052CD09 /* OCMockObject.m in Sources */,
03B315F4146333C00052CD09 /* OCMStubRecorder.m in Sources */,
03B315F9146333C00052CD09 /* OCMPassByRefSetter.m in Sources */,
Expand Down Expand Up @@ -1288,6 +1300,7 @@
817EB1241BD765130047E85A /* OCMBoxedReturnValueProvider.m in Sources */,
817EB1251BD765130047E85A /* OCMExceptionReturnValueProvider.m in Sources */,
817EB1261BD765130047E85A /* OCMIndirectReturnValueProvider.m in Sources */,
012412891D5A0B32003B75B8 /* OCProtocolsProxy.m in Sources */,
817EB1271BD765130047E85A /* OCMNotificationPoster.m in Sources */,
817EB1281BD765130047E85A /* OCMReturnValueProvider.m in Sources */,
817EB1291BD765130047E85A /* OCMLocation.m in Sources */,
Expand Down Expand Up @@ -1353,6 +1366,7 @@
F0B951191B0080EC00942C38 /* OCMBoxedReturnValueProvider.m in Sources */,
F0B9511A1B0080EC00942C38 /* OCMExceptionReturnValueProvider.m in Sources */,
F0B9511B1B0080EC00942C38 /* OCMIndirectReturnValueProvider.m in Sources */,
012412881D5A0B32003B75B8 /* OCProtocolsProxy.m in Sources */,
F0B9511C1B0080EC00942C38 /* OCMNotificationPoster.m in Sources */,
F0B9511D1B0080EC00942C38 /* OCMReturnValueProvider.m in Sources */,
F0B9511E1B0080EC00942C38 /* OCMLocation.m in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion Source/OCMock/OCClassMockObject.h
Expand Up @@ -22,7 +22,7 @@
Class originalMetaClass;
}

- (id)initWithClass:(Class)aClass;
- (id)initWithClass:(Class)aClass protocols:(NSArray *)protocols;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is way too invasive. At an absolute minimum we'd have to keep the original initWithClass: method, and make it forward to the proposed new initWithClass:protocols: method. That way, the number of changes in the PR would be greatly reduced.


- (Class)mockedClass;
- (Class)mockObjectClass; // since -class returns the mockedClass
Expand Down
36 changes: 29 additions & 7 deletions Source/OCMock/OCClassMockObject.m
Expand Up @@ -20,29 +20,44 @@
#import "OCMFunctionsPrivate.h"
#import "OCMInvocationStub.h"
#import "NSMethodSignature+OCMAdditions.h"
#import "OCProtocolsProxy.h"

@implementation OCClassMockObject
{
OCProtocolsProxy *protocolsProxy;
}

#pragma mark Initialisers, description, accessors, etc.

- (id)initWithClass:(Class)aClass
- (id)initWithClass:(Class)aClass protocols:(NSArray *)protocols
{
NSParameterAssert(aClass != nil);
[super init];
mockedClass = aClass;
[super init];
mockedClass = aClass;
protocolsProxy = [[OCProtocolsProxy alloc] initWithProtocols:protocols];

[self prepareClassForClassMethodMocking];
return self;
return self;
}

- (void)dealloc
{
[self stopMocking];
[protocolsProxy release];
[super dealloc];
}

- (NSString *)description
{
return [NSString stringWithFormat:@"OCMockObject(%@)", NSStringFromClass(mockedClass)];
NSArray *protocolNames = [protocolsProxy protocolNames];

if (protocolNames) {
return [NSString stringWithFormat:@"OCMockObject(%@ <%@>)",
NSStringFromClass(mockedClass),
[protocolNames componentsJoinedByString:@", "]];
}

return [NSString stringWithFormat:@"OCMockObject(%@)", NSStringFromClass(mockedClass)];
}

- (Class)mockedClass
Expand Down Expand Up @@ -186,10 +201,17 @@ - (void)initializeForClassObject
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
NSMethodSignature *signature = [mockedClass instanceMethodSignatureForSelector:aSelector];

if(signature == nil)
{
signature = [NSMethodSignature signatureForDynamicPropertyAccessedWithSelector:aSelector inClass:mockedClass];
}

if(signature == nil)
{
signature = [protocolsProxy methodSignatureForSelector:aSelector];
}

return signature;
}

Expand All @@ -205,7 +227,7 @@ - (Class)class

- (BOOL)respondsToSelector:(SEL)selector
{
return [mockedClass instancesRespondToSelector:selector];
return [mockedClass instancesRespondToSelector:selector] || [protocolsProxy respondsToSelector:selector];
}

- (BOOL)isKindOfClass:(Class)aClass
Expand All @@ -215,7 +237,7 @@ - (BOOL)isKindOfClass:(Class)aClass

- (BOOL)conformsToProtocol:(Protocol *)aProtocol
{
return class_conformsToProtocol(mockedClass, aProtocol);
return class_conformsToProtocol(mockedClass, aProtocol) || [protocolsProxy conformsToProtocol:aProtocol];
}

@end
Expand Down
16 changes: 12 additions & 4 deletions Source/OCMock/OCMock.h
Expand Up @@ -24,14 +24,22 @@
#import <OCMock/NSNotificationCenter+OCMAdditions.h>
#import <OCMock/OCMFunctions.h>

#define _OCMNilProtocol() nil
#define _OCM_OCMProtocols(...) __VA_ARGS__, nil

#define OCMClassMock(cls) [OCMockObject niceMockForClass:cls]
#define _OCMConcatFirstTwoArgs(arg1, arg2, ...) arg1 ## arg2
#define _OCMCountProtocolsArgChooser(...) _OCMConcatFirstTwoArgs(__VA_ARGS__)

#define OCMStrictClassMock(cls) [OCMockObject mockForClass:cls]
#define _OCMProtocols() NilProtocol
#define _OCMProtocolsArgChooser(...) _OCMCountProtocolsArgChooser(_OCM, _OCMProtocols , ##__VA_ARGS__ ())

#define OCMProtocolMock(protocol) [OCMockObject niceMockForProtocol:protocol]
#define OCMClassMock(cls, protocol...) [OCMockObject niceMockForClass:cls protocols:_OCMProtocolsArgChooser(protocol)(protocol)]

#define OCMStrictProtocolMock(protocol) [OCMockObject mockForProtocol:protocol]
#define OCMStrictClassMock(cls, protocol...) [OCMockObject mockForClass:cls protocols:_OCMProtocolsArgChooser(protocol)(protocol)]

#define OCMProtocolMock(protocol...) [OCMockObject niceMockForProtocols:protocol, nil]

#define OCMStrictProtocolMock(protocol...) [OCMockObject mockForProtocols:protocol, nil]

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

Expand Down
8 changes: 4 additions & 4 deletions Source/OCMock/OCMockObject.h
Expand Up @@ -33,12 +33,12 @@
NSMutableArray *invocations;
}

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

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

+ (id)observerMock;

Expand Down
65 changes: 56 additions & 9 deletions Source/OCMock/OCMockObject.m
Expand Up @@ -44,32 +44,79 @@ + (void)initialize

#pragma mark Factory methods

+ (id)mockForClass:(Class)aClass
+ (id)mockForClass:(Class)aClass protocols:(Protocol *)aProtocol, ...
{
return [[[OCClassMockObject alloc] initWithClass:aClass] autorelease];
va_list protocolsList;
va_start(protocolsList, aProtocol);

NSArray *protocols = [self _arrayOfProtocol:aProtocol otherProtocols:protocolsList];

va_end(protocolsList);

return [[[OCClassMockObject alloc] initWithClass:aClass protocols:protocols] autorelease];
}

+ (id)mockForProtocol:(Protocol *)aProtocol
+ (id)mockForProtocols:(Protocol *)aProtocol, ...
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above. This change is way too invasive. At an absolute minimum we'd have to keep the original mockForProtocol: method, and make it forward to the proposed new mockForProtocols: method. That way, the number of changes in the PR would be greatly reduced.

{
return [[[OCProtocolMockObject alloc] initWithProtocol:aProtocol] autorelease];
va_list protocolsList;
va_start(protocolsList, aProtocol);

NSArray *protocols = [self _arrayOfProtocol:aProtocol otherProtocols:protocolsList];

va_end(protocolsList);

return [[[OCProtocolMockObject alloc] initWithProtocols:protocols] autorelease];
}

+ (id)partialMockForObject:(NSObject *)anObject
{
return [[[OCPartialMockObject alloc] initWithObject:anObject] autorelease];
}


+ (id)niceMockForClass:(Class)aClass
+ (id)niceMockForClass:(Class)aClass protocols:(Protocol *)aProtocol, ...
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above.

{
return [self _makeNice:[self mockForClass:aClass]];
va_list protocolsList;
va_start(protocolsList, aProtocol);

NSArray *protocols = [self _arrayOfProtocol:aProtocol otherProtocols:protocolsList];

va_end(protocolsList);

id mock = [[[OCClassMockObject alloc] initWithClass:aClass protocols:protocols] autorelease];
return [self _makeNice:mock];
}

+ (id)niceMockForProtocol:(Protocol *)aProtocol
+ (id)niceMockForProtocols:(Protocol *)aProtocol, ...
{
return [self _makeNice:[self mockForProtocol:aProtocol]];
va_list protocolsList;
va_start(protocolsList, aProtocol);

NSArray *protocols = [self _arrayOfProtocol:aProtocol otherProtocols:protocolsList];

va_end(protocolsList);

id mock = [[[OCProtocolMockObject alloc] initWithProtocols:protocols] autorelease];

return [self _makeNice:mock];
}

+ (NSArray *)_arrayOfProtocol:(Protocol *)aProtocol otherProtocols:(va_list)protocolsList
{
if (!aProtocol)
{
return nil;
}

NSMutableArray *protocols = [NSMutableArray new];

while(aProtocol)
{
[protocols addObject:aProtocol];
aProtocol = va_arg(protocolsList, typeof(aProtocol));
}

return [protocols autorelease];
}

+ (id)_makeNice:(OCMockObject *)mock
{
Expand Down
2 changes: 1 addition & 1 deletion Source/OCMock/OCPartialMockObject.m
Expand Up @@ -31,7 +31,7 @@ - (id)initWithObject:(NSObject *)anObject
{
NSParameterAssert(anObject != nil);
[self assertClassIsSupported:[anObject class]];
[super initWithClass:[anObject class]];
[super initWithClass:[anObject class] protocols:nil];
realObject = [anObject retain];
[self prepareObjectForInstanceMethodMocking];
return self;
Expand Down
5 changes: 1 addition & 4 deletions Source/OCMock/OCProtocolMockObject.h
Expand Up @@ -17,11 +17,8 @@
#import <OCMock/OCMockObject.h>

@interface OCProtocolMockObject : OCMockObject
{
Protocol *mockedProtocol;
}

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

@end

36 changes: 21 additions & 15 deletions Source/OCMock/OCProtocolMockObject.m
Expand Up @@ -17,47 +17,53 @@
#import <objc/runtime.h>
#import "NSMethodSignature+OCMAdditions.h"
#import "OCProtocolMockObject.h"
#import "OCProtocolsProxy.h"

@implementation OCProtocolMockObject
{
OCProtocolsProxy *protocolsProxy;
}

#pragma mark Initialisers, description, accessors, etc.

- (id)initWithProtocol:(Protocol *)aProtocol
- (id)initWithProtocols:(NSArray *)protocols
{
NSParameterAssert(aProtocol != nil);
NSCParameterAssert(protocols != nil);

[super init];
mockedProtocol = aProtocol;

protocolsProxy = [[OCProtocolsProxy alloc] initWithProtocols:protocols];

return self;
}

- (void)dealloc
{
[protocolsProxy release];
[super dealloc];
}

- (NSString *)description
{
const char* name = protocol_getName(mockedProtocol);
return [NSString stringWithFormat:@"OCMockObject(%s)", name];
NSArray *protocolNames = [protocolsProxy protocolNames];
return [NSString stringWithFormat:@"OCMockObject(%@)", [protocolNames componentsJoinedByString:@", "]];
}

#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++)
{
struct objc_method_description methodDescription = protocol_getMethodDescription(mockedProtocol, aSelector, opts[i].isRequired, opts[i].isInstance);
if(methodDescription.name != NULL)
return [NSMethodSignature signatureWithObjCTypes:methodDescription.types];
}
return nil;
return [protocolsProxy methodSignatureForSelector:aSelector];
}

- (BOOL)conformsToProtocol:(Protocol *)aProtocol
{
return protocol_conformsToProtocol(mockedProtocol, aProtocol);
return [protocolsProxy conformsToProtocol:aProtocol];
}

- (BOOL)respondsToSelector:(SEL)selector
{
return ([self methodSignatureForSelector:selector] != nil);
return [protocolsProxy respondsToSelector:selector];
}

@end