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

Partial mocks use class created by OCClassMock #474

Open
wants to merge 1 commit 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions Source/OCMock/OCPartialMockObject.m
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,10 @@ - (void)stopMocking
{
if(realObject != nil)
{
Class partialMockClass = object_getClass(realObject);
OCMSetAssociatedMockForObject(nil, realObject);
object_setClass(realObject, [self mockedClass]);
[realObject release];
realObject = nil;
OCMDisposeSubclass(partialMockClass);
}
[super stopMocking];
}
Expand Down Expand Up @@ -150,27 +148,30 @@ - (void)prepareObjectForInstanceMethodMocking
{
OCMSetAssociatedMockForObject(self, realObject);

/* dynamically create a subclass and set it as the class of the object */
Class subclass = OCMCreateSubclass(mockedClass, realObject);
object_setClass(realObject, subclass);
if(!classCreatedForNewMetaClass)
{
classCreatedForNewMetaClass = OCMCreateSubclass(mockedClass, mockedClass);
}
object_setClass(realObject, classCreatedForNewMetaClass);

/* point forwardInvocation: of the object to the implementation in the mock */
Method myForwardMethod = class_getInstanceMethod([self mockObjectClass], @selector(forwardInvocationForRealObject:));
IMP myForwardIMP = method_getImplementation(myForwardMethod);
class_addMethod(subclass, @selector(forwardInvocation:), myForwardIMP, method_getTypeEncoding(myForwardMethod));
class_addMethod(classCreatedForNewMetaClass, @selector(forwardInvocation:), myForwardIMP, method_getTypeEncoding(myForwardMethod));

/* do the same for forwardingTargetForSelector, remember existing imp with alias selector */
Method myForwardingTargetMethod = class_getInstanceMethod([self mockObjectClass], @selector(forwardingTargetForSelectorForRealObject:));
IMP myForwardingTargetIMP = method_getImplementation(myForwardingTargetMethod);
IMP originalForwardingTargetIMP = [mockedClass instanceMethodForSelector:@selector(forwardingTargetForSelector:)];
class_addMethod(subclass, @selector(forwardingTargetForSelector:), myForwardingTargetIMP, method_getTypeEncoding(myForwardingTargetMethod));
class_addMethod(subclass, @selector(ocmock_replaced_forwardingTargetForSelector:), originalForwardingTargetIMP, method_getTypeEncoding(myForwardingTargetMethod));
const char *encoding = method_getTypeEncoding(myForwardingTargetMethod);
class_addMethod(classCreatedForNewMetaClass, @selector(forwardingTargetForSelector:), myForwardingTargetIMP, encoding);
class_addMethod(classCreatedForNewMetaClass, @selector(ocmock_replaced_forwardingTargetForSelector:), originalForwardingTargetIMP, encoding);

/* We also override the -class method to return the original class */
Method myObjectClassMethod = class_getInstanceMethod([self mockObjectClass], @selector(classForRealObject));
const char *objectClassTypes = method_getTypeEncoding(myObjectClassMethod);
IMP myObjectClassImp = method_getImplementation(myObjectClassMethod);
class_addMethod(subclass, @selector(class), myObjectClassImp, objectClassTypes);
class_addMethod(classCreatedForNewMetaClass, @selector(class), myObjectClassImp, objectClassTypes);

/* Adding forwarder for most instance methods to allow for verify after run */
NSArray *methodBlackList = @[ @"class", @"forwardingTargetForSelector:", @"methodSignatureForSelector:", @"forwardInvocation:",
Expand Down
52 changes: 0 additions & 52 deletions Source/OCMockTests/OCMockObjectPartialMocksTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -61,44 +61,6 @@ - (void)bar:(id)someArgument // maybe we should make it explicit that the arg is

@end

@interface TestClassThatObservesFoo : NSObject
{
@public
id observedObject;
}
@end

@implementation TestClassThatObservesFoo

- (instancetype)initWithObject:(id)object
{
if((self = [super init]))
observedObject = object;
return self;
}

- (void)dealloc
{
[self stopObserving];
}

- (void)startObserving
{
[observedObject addObserver:self forKeyPath:@"foo" options:0 context:NULL];
}

- (void)stopObserving
{
if(observedObject != nil)
{
[observedObject removeObserver:self forKeyPath:@"foo" context:NULL];
observedObject = nil;
}
}

@end


@interface TestClassThatCallsSelf : NSObject
{
int methodInt;
Expand Down Expand Up @@ -767,18 +729,4 @@ - (void)testDoesNotIncludeHintWhenStubbingIsNotGoingToHelp
}
}

- (void)testThrowsExceptionWhenAttemptingToTearDownWrongClass
{
TestClassWithSimpleMethod *realObject = [[TestClassWithSimpleMethod alloc] init];
TestClassThatObservesFoo *observer = [[TestClassThatObservesFoo alloc] initWithObject:realObject];
id mock = [OCMockObject partialMockForObject:realObject];
[observer startObserving];

// If we invoked stopObserving here, then stopMocking would work; but we want to test the error case.
XCTAssertThrowsSpecificNamed([mock stopMocking], NSException, NSInvalidArgumentException);

// Must reset the object here to avoid any attempt to remove the observer, which would fail.
observer->observedObject = nil;
}

@end