erikdoe / ocmock Goto Github PK
View Code? Open in Web Editor NEWMock objects for Objective-C
Home Page: http://ocmock.org
License: Apache License 2.0
Mock objects for Objective-C
Home Page: http://ocmock.org
License: Apache License 2.0
I would like to mock alloc
then set up a stub on the mocked instance. However, the stub is being removed when alloc is called.
Best illustrated by an example:
id stringClassMock = [OCMockObject niceMockForClass:[NSString class]];
[[[stringClassMock stub] andReturn:stringClassMock] alloc];
[[[stringClassMock stub] andDo:^(NSInvocation * i) {
NSLog(@"This is not not called :( ");
}] enumerateLinesUsingBlock:OCMOCK_ANY];
// This line causes above stub to be removed,
// If you comment out this line it works.
NSString * myNewString = [[NSString alloc] init];
[stringClassMock enumerateLinesUsingBlock:nil];
Using OCMock (2.2.4)
This is when creating a mock for a plain old object
XCode 5.1.1, unit testing in simulator 64-bit
* thread #1: tid = 0x169149, 0x0000000103593973 libobjc.A.dylib`objc_exception_throw, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
* frame #0: 0x0000000103593973 libobjc.A.dylib`objc_exception_throw
frame #1: 0x0000000103818cb4 CoreFoundation`__NSGetSizeAndAlignment + 2628
frame #2: 0x0000000103818745 CoreFoundation`__NSGetSizeAndAlignment + 1237
frame #3: 0x000000010382457f CoreFoundation`__NSMS1 + 63
frame #4: 0x000000010382492e CoreFoundation`__NSMS1 + 1006
frame #5: 0x0000000103823ead CoreFoundation`+[NSMethodSignature signatureWithObjCTypes:] + 749
frame #6: 0x000000010f228bdb <REDACTED>Tests`+[NSObject(self=0x000000010d5b2ae0, _cmd=0x000000010d302400, aSelector=0x00000001021f482c) instanceMethodForwarderForSelector:] + 59 at NSObject+OCMAdditions.m:29
frame #7: 0x000000010f229446 <REDACTED>Tests`-[OCClassMockObject setupForwarderForClassMethodSelector:](self=0x000000010d568800, _cmd=0x000000010d3023a0, selector=0x00000001021f482c) + 118 at OCClassMockObject.m:110
frame #8: 0x000000010f229315 <REDACTED>Tests`__54-[OCClassMockObject prepareClassForClassMethodMocking]_block_invoke(.block_descriptor=<unavailable>, selector=0x00000001021f482c) + 101 at OCClassMockObject.m:99
frame #9: 0x000000010f228cbf <REDACTED>Tests`+[NSObject(self=0x000000010373cc78, _cmd=0x000000010d3023d0, aClass=0x0000000100736c88, aBlock=0x00007fff5fbfc028) enumerateMethodsInClass:usingBlock:] + 143 at NSObject+OCMAdditions.m:48
frame #10: 0x000000010f22927f <REDACTED>Tests`-[OCClassMockObject prepareClassForClassMethodMocking](self=0x000000010d568800, _cmd=0x000000010d302300) + 831 at OCClassMockObject.m:97
frame #11: 0x000000010f228d64 <REDACTED>Tests`-[OCClassMockObject initWithClass:](self=0x000000010d568800, _cmd=0x000000010a7f9404, aClass=0x0000000100736c60) + 100 at OCClassMockObject.m:31
frame #12: 0x000000010f22e46d <REDACTED>Tests`+[OCMockObject mockForClass:](self=0x000000010f3243a0, _cmd=0x0000000100527ac7, aClass=0x0000000100736c60) + 61 at OCMockObject.m:48
frame #13: 0x000000010f043ec5 <REDACTED>Tests`__29-[MRAssetSpec spt_defineSpec]_block_invoke(.block_descriptor=0x000000010d5cf640) + 229 at MRAssetSpecs.m:22
frame #14: 0x000000010f239d79 <REDACTED>Tests`runExampleBlock(block=0x000000010d5cf640, name=0x0000000114915410) + 1449 at SPTExampleGroup.m:70
frame #15: 0x000000010f239611 <REDACTED>Tests`-[SPTExampleGroup runBeforeHooks:](self=0x000000010d5cf710, _cmd=0x000000010d303030, compiledName=0x000000010d5cf9f0) + 1969 at SPTExampleGroup.m:245
frame #16: 0x000000010f23b200 <REDACTED>Tests`__48-[SPTExampleGroup compileExamplesWithNameStack:]_block_invoke(.block_descriptor=<unavailable>) + 144 at SPTExampleGroup.m:305
frame #17: 0x000000010f24001d <REDACTED>Tests`-[SPTXCTestCase spt_runExampleAtIndex:](self=0x000000010e6ae610, _cmd=0x000000010d303580, index=0) + 509 at SPTXCTestCase.m:95
frame #18: 0x0000000103829f1c CoreFoundation`__invoking___ + 140
frame #19: 0x0000000103829dc4 CoreFoundation`-[NSInvocation invoke] + 308
frame #20: 0x000000010d726c40 XCTest`-[XCTestCase invokeTest] + 161
frame #21: 0x000000010d726d2c XCTest`-[XCTestCase performTest:] + 91
frame #22: 0x000000010f2408f9 <REDACTED>Tests`-[SPTXCTestCase performTest:](self=0x000000010e6ae610, _cmd=0x000000010d72a71c, run=0x000000010d5befe0) + 137 at SPTXCTestCase.m:147
frame #23: 0x000000010d727a75 XCTest`-[XCTest run] + 65
frame #24: 0x000000010d7264df XCTest`-[XCTestSuite performTest:] + 125
frame #25: 0x000000010d727a75 XCTest`-[XCTest run] + 65
frame #26: 0x000000010d7264df XCTest`-[XCTestSuite performTest:] + 125
frame #27: 0x000000010d727a75 XCTest`-[XCTest run] + 65
frame #28: 0x000000010d7264df XCTest`-[XCTestSuite performTest:] + 125
frame #29: 0x000000010d727a75 XCTest`-[XCTest run] + 65
frame #30: 0x000000010d7291b4 XCTest`+[XCTestProbe runTests:] + 138
frame #31: 0x00000001016e36dc Foundation`__NSFireDelayedPerform + 354
frame #32: 0x00000001037f6c34 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 20
frame #33: 0x00000001037f67b2 CoreFoundation`__CFRunLoopDoTimer + 962
frame #34: 0x00000001037df7be CoreFoundation`__CFRunLoopRun + 1614
frame #35: 0x00000001037ded83 CoreFoundation`CFRunLoopRunSpecific + 467
frame #36: 0x0000000104a0cf04 GraphicsServices`GSEventRunModal + 161
frame #37: 0x000000010243ee33 UIKit`UIApplicationMain + 1010
frame #38: 0x0000000100088675 <REDACTED>`main(argc=8, argv=0x00007fff5fbfec60) + 181 at main.m:16
The documentation just says:
NOTE: this should have a modern syntax.
I am trying to stub the same mock object more than once. I debugged the case and find out that Partial Mock objects are failing to stub properties after stubbing once.
- (void)testSomething{
id partialMock = [OCMockObject partialMockForObject:self.someObject];
[[[partialMock stub] andReturnValue:OCMOCK_VALUE(value)] someProperty];
XCTAssertTrue(self.someObject.someProperty == value); // This test is passing
[[[partialMock stub] andReturnValue:OCMOCK_VALUE(someOtherValue)] someProperty];
XCTAssertTrue(self.someObject.someProperty == someOtherValue); // This test is failing
}
I ran into a case where a class mock appears to be ignored (or replaced?) when a mock is created for a previously created instance of the class. This worked with OCMock 2.
https://www.dropbox.com/s/mioeo72vow9sgz2/OCMockClassMocking.m
*The exception occurs in this method:
(... NSObject+OCMAdditions.m ...)
+ (IMP)instanceMethodForwarderForSelector:(SEL)aSelector
{
// use NSSelectorFromString and not @selector to avoid warning
SEL selectorWithNoImplementation = NSSelectorFromString(@"methodWhichMustNotExist::::");
#ifndef __arm64__
NSMethodSignature *sig = [self instanceMethodSignatureForSelector:aSelector];
if([sig usesSpecialStructureReturn])
return class_getMethodImplementation_stret(self, selectorWithNoImplementation);
#endif
return class_getMethodImplementation(self, selectorWithNoImplementation);
}
at the following point of the method:
NSMethodSignature *sig = [self instanceMethodSignatureForSelector:aSelector];
*It is triggered by calling this method:
id mockView = OCMClassMock([UIView class]);
This happens off and on. I can get it sometimes and others not. I'm not even using CoreLocation so have no idea why this is happening but it is flaky and we did not have it w/ OCMock 2.
The expect
is actually passing but this keeps happening. When I remove the stopMocking
in the afterEach
, everything works exactly as expected. Any thoughts?
Log:
2014-07-17 18:14:12.604 App[4084:60b] CLTilesManagerClient: initialize, sSharedTilesManagerClient
2014-07-17 18:14:12.605 App[4084:60b] CLTilesManagerClient: init
2014-07-17 18:14:12.605 App[4084:60b] CLTilesManagerClient: reconnecting, 0xa9e9360
Here is the test:
it(@"should encrypt data", ^{
NSString *key = @"key";
NSData *unencrypedData = [@"encryptme" dataUsingEncoding:NSUTF8StringEncoding];
NSData *encryptedData = [unencrypedData AES256EncryptedDataUsingKey:key error:nil];
OCMStub([staticSubjectMock encryptionKey]).andReturn(key);
NSData *newData = [WFCache encryptData:unencrypedData];
expect(newData).to.equal(encryptedData);
});
Here is the code:
+ (NSData*)encryptData:(NSData*)dataToEncrypt {
NSError *encryptionError;
NSData *encryptedData = [dataToEncrypt AES256EncryptedDataUsingKey:[WFCache encryptionKey]
error:&encryptionError];
if (encryptionError) {
NSLog(@"%@ - ERROR encrpyting data", TAG);
return nil;
} else {
return encryptedData;
}
}
The only other thing we do is a beforeEach
:
beforeEach(^{
subject = [[WFCache alloc] init];
subjectMock = OCMPartialMock(subject);
});
I've been trying to track down some problems in my tests relating to mock related exceptions in OCMGetAssociatedMockForClass
. After some fiddling and disabling of tests I've narrowed replication down.
The following code will work perfectly so long as the TABAPIRequestOperation
class has not been class mocked before:
apiRequestOperation = OCMClassMock(TABAPIRequestOperation.class);
request = OCMPartialMock(request);
OCMStub([request createAPIRequestOperation]).andReturn(apiRequestOperation);
OCMStub([request startAfterDelay:0 originalRepeatInterval:0]);
This is the case if a previous spec that mocks TABAPIRequestOperation
has been disabled. I can replicate the exception "No mock for class (null)"
by creating a Class mock for the Partially Mocked Object of the same Class immediately before:
OCMClassMock(TABAPIPollingRequestOperation.class);
apiRequestOperation = OCMClassMock(TABAPIRequestOperation.class);
request = OCMPartialMock(request);
OCMStub([request createAPIRequestOperation]).andReturn(apiRequestOperation);
OCMStub([request startAfterDelay:0 originalRepeatInterval:0]);
In order to track down the source of this, I've placed some logging code in OCMGetAssociatedMockForClass
:
OCClassMockObject *OCMGetAssociatedMockForClass(Class aClass)
{
if(![NSThread isMainThread]) {
[NSException raise:NSInternalInconsistencyException format:@"NAUGHTY CONCURRENCY"];
}
NSLog(@"Obtaining Associated Mock for Class %@", NSStringFromClass(aClass));
OCClassMockObject *mock = nil;
while((mock == nil) && (aClass != nil))
{
mock = objc_getAssociatedObject(aClass, OCMClassMethodMockObjectKey);
aClass = class_getSuperclass(aClass);
NSLog(@"Mock %@ for class %@", mock, NSStringFromClass(aClass));
}
if(mock == nil)
[NSException raise:NSInternalInconsistencyException format:@"No mock for class %@", NSStringFromClass(aClass)];
return mock;
}
This confirms that there is no non-main thread access to elimiate concurrent access as the source of the problem as well as prints the inheritance chain right before the before the crash:
2014-05-30 08:25:01.238 SunTablet[53580:60b] Obtaining Associated Mock for Class TABAPIPollingRequestOperation
2014-05-30 08:25:01.238 SunTablet[53580:60b] Mock (null) for class NSOperation
2014-05-30 08:25:01.238 SunTablet[53580:60b] Mock (null) for class NSObject
2014-05-30 08:25:01.239 SunTablet[53580:60b] Mock (null) for class (null)
And here is the backtrace:
(lldb) bt
* thread #1: tid = 0x8c0ea, 0x0d03e672 SunTabletTests`OCMGetAssociatedMockForClass(aClass=0x00000000) + 226 at OCMFunctions.m:139, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
* frame #0: 0x0d03e672 SunTabletTests`OCMGetAssociatedMockForClass(aClass=0x00000000) + 226 at OCMFunctions.m:139
frame #1: 0x0d03cb7d SunTabletTests`-[OCClassMockObject forwardInvocationForClassObject:](self=0x0d07bc6c, _cmd=0x019e471b, anInvocation=0x0ba6ec90) + 45 at OCClassMockObject.m:112
frame #2: 0x01fe52da CoreFoundation`___forwarding___ + 458
frame #3: 0x01fe50ee CoreFoundation`__forwarding_prep_0___ + 14
frame #4: 0x0d03c5f4 SunTabletTests`-[OCClassMockObject prepareClassForClassMethodMocking](self=0x116e7a50, _cmd=0x0bc52820) + 132 at OCClassMockObject.m:71
frame #5: 0x0d03c391 SunTabletTests`-[OCClassMockObject initWithClass:](self=0x116e7a50, _cmd=0x0456c7bc, aClass=0x0d07bc6c) + 113 at OCClassMockObject.m:31
frame #6: 0x0d040e76 SunTabletTests`+[OCMockObject mockForClass:](self=0x0d07c374, _cmd=0x0bc52ab0, aClass=0x0d07bc6c) + 86 at OCMockObject.m:46
frame #7: 0x0d040fe5 SunTabletTests`+[OCMockObject niceMockForClass:](self=0x0d07c374, _cmd=0x0bc52060, aClass=0x0d07bc6c) + 69 at OCMockObject.m:62
frame #8: 0x0d00d20a SunTabletTests`__51-[TABAPIPollingRequestOperationSpec spt_defineSpec]_block_invoke2(.block_descriptor=0x116de030) + 394 at TABAPIPollingRequestSpec.m:40
frame #9: 0x0d04e6fc SunTabletTests`runExampleBlock(block=0x116de030, name=0x116edc80) + 1500 at SPTExampleGroup.m:70
frame #10: 0x0d04df23 SunTabletTests`-[SPTExampleGroup runBeforeHooks:](self=0x116de170, _cmd=0x0bc535a0, compiledName=0x116de520) + 2083 at SPTExampleGroup.m:245
frame #11: 0x0d04fcd8 SunTabletTests`__48-[SPTExampleGroup compileExamplesWithNameStack:]_block_invoke(.block_descriptor=<unavailable>) + 152 at SPTExampleGroup.m:305
frame #12: 0x0d054e8c SunTabletTests`-[SPTXCTestCase spt_runExampleAtIndex:](self=0x116e1ea0, _cmd=0x0bc53b00, index=1) + 556 at SPTXCTestCase.m:95
frame #13: 0x01fe991d CoreFoundation`__invoking___ + 29
frame #14: 0x01fe982a CoreFoundation`-[NSInvocation invoke] + 362
frame #15: 0x20103c6c XCTest`-[XCTestCase invokeTest] + 221
frame #16: 0x20103d7b XCTest`-[XCTestCase performTest:] + 111
frame #17: 0x0d055878 SunTabletTests`-[SPTXCTestCase performTest:](self=0x116e1ea0, _cmd=0x20107ed4, run=0x0ba6b900) + 152 at SPTXCTestCase.m:147
frame #18: 0x20104c48 XCTest`-[XCTest run] + 82
frame #19: 0x201033e8 XCTest`-[XCTestSuite performTest:] + 139
frame #20: 0x20104c48 XCTest`-[XCTest run] + 82
frame #21: 0x201033e8 XCTest`-[XCTestSuite performTest:] + 139
frame #22: 0x20104c48 XCTest`-[XCTest run] + 82
frame #23: 0x201033e8 XCTest`-[XCTestSuite performTest:] + 139
frame #24: 0x20104c48 XCTest`-[XCTest run] + 82
frame #25: 0x201066ba XCTest`+[XCTestProbe runTests:] + 183
frame #26: 0x004e05ec Foundation`__NSFireDelayedPerform + 372
frame #27: 0x01fb3ac6 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 22
frame #28: 0x01fb34ad CoreFoundation`__CFRunLoopDoTimer + 1181
frame #29: 0x01f9b538 CoreFoundation`__CFRunLoopRun + 1816
frame #30: 0x01f9a9d3 CoreFoundation`CFRunLoopRunSpecific + 467
frame #31: 0x01f9a7eb CoreFoundation`CFRunLoopRunInMode + 123
frame #32: 0x03d2d5ee GraphicsServices`GSEventRunModal + 192
frame #33: 0x03d2d42b GraphicsServices`GSEventRun + 104
frame #34: 0x00ab4f9b UIKit`UIApplicationMain + 1225
frame #35: 0x000040ad SunTablet`main(argc=8, argv=0xbfffed5c) + 141 at main.m:14
Interestingly this will only occur when the second code snippet has occured twice or more. If only one test case depends on this snippet occuring, there will be no exception thron This leads me to believe that there is some cleanup that is not occuring, though I am not that wise to the internals of OCMock
. The documents say and I have verified that stopMocking:
is called on the partial on dealloc, I've also added stopMocking
to an afterEach
to ensure teardown after each test.
Partial Mocks have their sharp edges, but in this case they are the 'least worst' incremental step to build out tests with a view to move towards less coupled and therefore more testable classes in the future.
I am writing unit tests for code that includes objective-c and c++ files, and I would like to leverage OCMock 3.0.2's "verify-after-running" feature. However, I am getting a linker error. Would like this to be fixed.
Detail information on stackoverflow: http://stackoverflow.com/questions/24928605/ocmock-3-0-2-linker-error-with-mm-test-file
Hi guys,
I'm trying to create a stub for a class method as explained in the documentation.
id mock = OCMClassMock([MyClass class]);
[[[mock stub] andReturn:mock] myFactoryWithParameter:[OCMArg any]];
When I try to call the factory in test code(into the method that specifies the test) everything works fine and I get the mock object as expected, but when I call the same factory method inside another method that is called by the test code, I get the "normal" object. It seems the stub is not executed.
[I get the mock]
- (void)test_somethingToTest
{
id mock = OCMClassMock([MyClass class]);
[[[mock stub] andReturn:mock] myFactoryWithParameter:[OCMArg any]];
// "object" contains the mock
MyClass *object = [MyClass myFactoryWithParameter:@"string"];
}
[I get the object as usual]
- (void)test_somethingToTest
{
id mock = OCMClassMock([MyClass class]);
[[[mock stub] andReturn:mock] myFactoryWithParameter:[OCMArg any]];
[objectUnderTest someMethod];
}
...
- (void)someMethod
{
// "object" contains the original object
MyClass *object = [MyClass myFactoryWithParameter:@"string"];
}
I don't know if it is a bug or not, probably I'm doing something wrong. Any idea?
Thanks.
I'm writing test cases for code based on AFNetworking and using OCMock to do this. If I implement my stub like this:
OCMStub([mockRemoteStore POST:[OCMArg any] parameters:[OCMArg any] success:[OCMArg any] failure:[OCMArg any]]).andCall(self, @
selector(POST:parameters:success:failure:));
- (NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(id)parameters
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure {
if (failure) {
ExHTTPRemoteDataStore *remoteStore = [TestStore store].remoteHTTPDataStore;
NSError *error = [NSError errorWithDomain:@"Mock Error Domain" code:1234 userInfo:@{}];
failure(nil, error);
}
return nil;
}
Everything works as expected.
But if I use .andDo(...)
:
OCMStub([mockRemoteStore POST:[OCMArg any] parameters:[OCMArg any] success:[OCMArg any] failure:[OCMArg any]]).andDo(^(NSInvocation *invocation) {
[self handlePOSTInvocation:invocation];
});
- (void)handlePOSTInvocation:(NSInvocation *)invocation {
ExHTTPRemoteDataStore *remoteStore = [TestStore store].remoteHTTPDataStore;
AFNDataTaskSuccessBlock successBlock = nil;
AFNDataTaskFailureBlock failureBlock = nil;
[invocation getArgument:&successBlock atIndex:4];
[invocation getArgument:&failureBlock atIndex:5];
NSError *error = [NSError errorWithDomain:@"Mock Error Domain" code:1234 userInfo:@{}];
failureBlock(nil, error);
NSURLSessionDataTask *task = nil;
[invocation setReturnValue:&task];
}
The segmentation fault occurs after/during the call to the failure block. I can't really pinpoint the actual error, but it certainly has to do with either the way I used the NSInvocation *invocation
or something in OCMock. I'm using the latest code from the Master branch (downloaded on Aug 6). Is there something I'm obviously doing wrong?
I'm having a very similar problem to #68 - I'm using partial mocks of NSManagedObjects to test my core data models.
At some places, I'm using real NSManagedObjects and that works great, at others I need to mock certain methods that produce computed values out of the model data, because they depend on a deep hierarchy of related NSManagedObjects.
When I mock these methods however, properties of these objects cannot be used anymore because there seems to be no way at the moment to mock them or set them.
What do you think about optionally having "nice" partial mocks, which behave just like the real objects except for the methods that have been mocked via andReturnValue:
?
I have some code which should only post a notification under certain circumstances. While observer mocks work great for testing if a notification was posted, they don't work for testing that a notification wasn't sent as they lack a -reject method.
I am using OCMOCK 2.2.3 and when I did this:
[[mock expect] GET:[OCMArg any] success:[OCMArg any] failure:[OCMArg any]];
I got this: -[OCMAnyConstraint copyWithZone:]: unrecognized selector sent to instance 0x8ab8990
It only happens when a parameter is a block, in this case success and failure parameters are blocks.
I think it doesn't happen in previous versions.
id mock = OCMStrictClassMock([SomeClass class]);
OCMStub([mock someMethod]).andReturn(@"a string");
OCMExpect([mock someMethod]);
/* run code under test */
OCMVerifyAll(); // will complain that someMethod has not been called
OCMVerifyAll
requires an argument [mock
in this case].
The following is causing an infinite recursive loop when I execute my test in Xcode:
// This is inside a larger test method
self.mockSharing = [OCMockObject mockForClass:[Sharing class]];
[[[self.mockSharing expect] classMethod] shareMessage:message trackingString:[OCMArg isNil]];
[Sharing shareMessage:message];
////////
// This is the method that eventually expects shareMessage:trackingString: to occur
+ (void)shareMessage:(Message *)message
{
if (!message)
{
return;
}
__block NSString *body = message.body;
void (^complete)(void) = ^ {
[Sharing shareMessage:body trackingString:nil];
};
[Shortener shortenURLString:message.publicUrl withResponseBlock:^(NSURL *shortUrl, NSError *error) {
if (shortUrl && !error)
{
body = [body stringByAppendingString:[NSString stringWithFormat:@" %@", shortUrl.absoluteString]];
}
complete();
}];
}
@interface Hoge : NSObject
@property (nonatomic) NSInteger fuga;
@end
id hogeMock = [OCMockObject mockForClass:[Hoge class]];
[[[hogeMock stub] andReturnValue:@(100)] fuga];
The log is below.
failed: Return value does not match method signature; signature declares 'q' but value is 'i'.
Could you tell me the solution?
I have not tried including OCMock into my project manually, any expectation that would have a different effect?
If I include OCMock 3.x with my podfile, I cannot compile my project. OCMock 2.x does work this way.
ld: framework not found XCTest
clang: error: linker command failed with exit code 1 (use -v to see invocation)
When testing Chrome on iOS, we found that mocked properties returning a C++ object (in our case a GURL) by pointer failed when building using libc++ and clang. It appears that the type descriptions returned by @encode(typeof(expr)) (from OCMOCK_VALUE) and -[[NSInvocation methodSignature] methodReturnType] have their properties recursively expanded, but with different depth, causing the strcmp to fail.
The code looks like:
@interface SomeClass
@Property (nonatomic, assign) const GURL* origin;
@EnD
const GURL kFakeOrigin = ....;
const GURL* fake_origin_ptr = &kFakeOrigin;
[[[mock_class stub] andReturnValue:OCMOCK_VALUE(fake_origin_ptr)] origin];
And the type descriptions in OCMBoxedReturnValueProvider.m are:
(lldb) p returnType
(const char _) $0 = 0x149e901d "r^{GURL={basic_string<char, std::__1::char_traits, std::__1::allocator >={__compressed_pair<std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator >::__rep, std::__1::allocator >={__rep}}}B{Parsed={Component=ii}{Component=ii}{Component=ii}{Component=ii}{Component=ii}{Component=ii}{Component=ii}{Component=ii}^{Parsed}}{scoped_ptr<GURL, base::DefaultDeleter >={scoped_ptr_impl<GURL, base::DefaultDeleter >={Data=^{GURL}}}}}"
(lldb) p valueType
(const char *) $1 = 0x149e5808 "r^{GURL={basic_string<char, std::__1::char_traits, std::__1::allocator >={__compressed_pair<std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator >::__rep, std::__1::allocator >={__rep=(?={_long=II}{__short=(?=Cc)[11c]}{__raw=[3L]})}}}B{Parsed={Component=ii}{Component=ii}{Component=ii}{Component=ii}{Component=ii}{Component=ii}{Component=ii}{Component=ii}^{Parsed}}{scoped_ptr<GURL, base::DefaultDeleter >={scoped_ptr_impl<GURL, base::DefaultDeleter >={Data=^{GURL}}}}}"
When using andCall: on a stub, and returning an autoreleased object from within the method that is called, that object will end up being destroyed too soon.
This is apparently caused by the fact that the invocation is done from within an -[NSArray indexOfObjectPassingTest:] in -[OCMockObject handleInvocation:]. It looks like the block is wrapped in an autorelease pool, and so the object will be released as soon as the invocation is done, and before even returning to the actual caller...
file:///%3Cunknown%3E: test failure: -[WFViewControllerSpec UIViewController_WFViewController_h_openModalView_should_open_a_view_from_the_storyboard] failed: There doesn't seem to be a valid compiled storyboard at path '/Users/johnbland/Library/Application Support/iPhone Simulator/7.1/Applications/9C594DFC-A063-421B-9169-DBE5F4324D6C/App.app/App.storyboardc'
This happens a ton. Comment out all NSFileManager
mocks and it works as expected. Call stopMocking
on the mock and it works. From the discussion on #118 I took away we should never need to do this.
Mock:
fileManagerMock = OCMClassMock([NSFileManager class]);
OCMStub([fileManagerMock defaultManager]).andReturn(fileManagerMock);
Then:
afterEach(^{
OCMVerifyAll(fileManagerMock);
[fileManagerMock stopMocking];
});
That's the only solution. Thoughts?
With OCMock i can't return nil
on partial mocks:
id mock = OCMPartialMock([SomeManager sharedInstance]);
OCMStub([mock objectForKeyPath:@"some.keyPath"]).andReturn(nil);
i get the following error:
Return value does not match method signature; signature declares '@' but value is '^v'.
With OCMock 2 it works fine.
Any thoughts?
As it OCMockito:
[verifyCount(sut, never()) someMethod];
I'm trying to stub an async method in a partial mock:
id mock = OCMPartialMock([[MyClass alloc] init]);
void (^theBlock)(NSInvocation *) = ^(NSInvocation *invocation) {
MyObjectWithAction *owa = nil;
[invocation getArgument:&owa atIndex:3];
if (owa && owa.action) {
owa.action();
}
};
OCMStub([mock myMethodWithParam:[OCMArg any]]).andDo(theBlock);
But inside "action()", I'm calling to "myMethodWithParam:" one more time (but this time with a "MyObjectWithAction" without action).
...and I'm getting:
malloc: *** error for object 0xdd59a20: pointer being freed was not allocated
I just can't get Class method mocking to work with the new syntax. This will work fine:
TABCoreDataBulkOperations *bulkOperations = OCMClassMock(TABCoreDataBulkOperations.class);
[[[[(id)bulkOperations stub] classMethod] andReturn:@"FOOBAR"] bulkOperationsOnManagedObjectClass:OCMOCK_ANY withCoreData:OCMOCK_ANY];
Any combination of the Macro based stubbing will result in either compiler errors, or the original implementation being called with OCMock constraints passed as arguments from the invocation.
OCMStub(ClassMethod([bulkOperations.class bulkOperationsOnManagedObjectClass:OCMOCK_ANY withCoreData:OCMOCK_ANY])).andReturn(@"FOOBAR"); //Original method called
OCMStub([bulkOperations.class bulkOperationsOnManagedObjectClass:OCMOCK_ANY withCoreData:OCMOCK_ANY]).andReturn(@"FOOBAR"); //Original method called
OCMStub(ClassMethod([TABCoreDataBulkOperations bulkOperationsOnManagedObjectClass:OCMOCK_ANY withCoreData:OCMOCK_ANY])).andReturn(@"FOOBAR"); //Original method called
OCMStub(ClassMethod([TABCoreDataBulkOperations.class bulkOperationsOnManagedObjectClass:OCMOCK_ANY withCoreData:OCMOCK_ANY])).andReturn(@"FOOBAR"); //Original method called
OCMStub([bulkOperations bulkOperationsOnManagedObjectClass:OCMOCK_ANY withCoreData:OCMOCK_ANY]).andReturn(@"FOOBAR"); //Compiler error
Is there something that I'm doing wrong here? It seems that one of these variants should result in the stub being assigned. The backtrace shows that the class mock is being hit, then running the invocation against the real object:
frame #3: 0x0bea54d5 Redacted`-[OCClassMockObject forwardInvocationForClassObject:](self=0x0bf0dd50, _cmd=0x01dfc71b, anInvocation=0x13c578f0) + 293 at OCClassMockObject.m:128
From the docs on OCMock.org, this should be working in master since M1.
3.0.0 builds fine
Imagine trying to sort some objects:
id object1 = [OCMockObject mockForProtocol:@protocol(MyModel)];
id object2 = [OCMockObject mockForProtocol:@protocol(MyModel)];
[[[object1 stub] andReturn:@"B"] name];
[[[object2 stub] andReturn:@"A"] name];
//...
XCTAssertEqualObjects(sortedArray, @[object2, object1]);
I don't want to depend on how the sort is implemented, just on whether it works. If I use sortedArrayUsingDescriptors:
then the test will fail because the implementation calls valueForKey:
rather than calling the accessor directly. What I'd like to do is something like this:
[[[object1 stub] value:@"B"] forKey:@"name"];
[[[object2 stub] value:@"A"] forKey:@"name"];
which is both a convenience for stubbing -name
, and stubs -valueForKey:
.
I am in the process of migrating to OCMock 3, but I have run into a problem where a weak pointer on an object becomes nil
after I create a partial mock for the pointed-to object. I have reproduced the behavior in a simple test case.
Am I misusing OCMock? This sort of usage has not been a problem with OCMock 2.
So far, I have seen this with Xcode 5.1.1 on OS X 10.9.4 when using the iOS simulator. It happens with both the 32-bit and 64-bit simulators.
It's my first use of your library and I might be missing something but I wanted to expect that a property has been accessed.
As the accessor is a method I thought that would work but not at all :(
Here is the code:
YSUser *user = [[YSUser alloc] init];
id mockUser = [OCMockObject partialMockForObject:user];
[[[mockUser stub] andReturn:nil] username];
[[mockUser expect] username];
expect([validator validate:mockUser error:nil]).to.equal(NO);
[mockUser verify];
Here is the error: OCPartialMockObject[YSUser]: expected method was not invoked: username
.
And my validate
looks like that:
- (BOOL)validate:(YSUser *)user error:(NSError *__autoreleasing *)error
{
NSParameterAssert(user != nil);
if ([user.username length] == 0) {
if (error) {
NSDictionary *userInfo = @{
YSUserValidatorPropertyKey: NSStringFromSelector(@selector(username)),
NSLocalizedDescriptionKey: @"mandatory property is empty"
};
*error = [NSError errorWithDomain:YSUserValidatorErrorDomain code:YSUserValidatorErrorMissingMandatoryProperty userInfo:userInfo];
}
return NO;
}
return YES;
}
Just in case I'm using specta and expecta.
Thanks
I have a object Controller
that has a property that returns a mock object. I then ask Controller
for valueForKeyPath:
where the path is a path to the mock object's property. I can see in the debugger that valueForKeyPath:
is correctly executed on the Controller
and that it calls valueForKey:
on the Controller
object which returns the mocked object. However, the mocked object never gets a call to valueForKeyPath:
or valueForKey:
.
This is my mock creation:
self.reservationMock = [OCMockObject niceMockForClass:[Reservation class]];
self.sut = [[Controller alloc] initWithReservation:self.reservationMock];
The mock is stored in reservation
. I evaluate a predicate with reservation.reservationMode
on the sut
.
I get the following calls:
valueForKeyPath:@"reservation.reservationMode"
valueForKey:@"reservation"
And that's it. If I do not mock the object, the evaluation continues with the following calls on the Reservation
object.
valueForKeyPath:@"reservationMode"
valueForKey:@"reservationMode"
I'm getting a BAD_ACCESS in the dealloc of OCMRecorder for a test involving asynchronous methods. It seems invocationMatcher is being over released. If I remove the [invocationMatcher release] it's fine. This is with Xcode 6, iOS 8.
If in one test method...
id mockObject = [OCMockObject partialMockForObject:realObject];
[[[mockObject stub] andReturn:mockObject] getObjectInstance];
[mockObject stopMocking]
...and then in a test in another class somewhere else that's run later...
id realObject = [ObjectClass getObjectInstance];
The supposedly unmocked call to the class method never happens. It cannot be stepped into, and a breakpoint inside it is not hit. nil
is returned.
The workaround is to use another mock object, separate to the partial mock, to add the class method stub.
In OCMock 3.0.2, a stub for a method that takes a parameter will:
In this test case, the first and third tests pass, while the second fails:
#import <XCTest/XCTest.h>
#import <OCMock/OCMock.h>
typedef NSString TypedefString;
@interface StubWithTypedefFailureDemo : NSObject
- (NSString *)stringForTypedef:(TypedefString *)string;
- (NSString *)stringForString:(NSString *)string;
@end
@implementation StubWithTypedefFailureDemo
- (NSString *)stringForString:(NSString *)string
{
NSAssert(NO, @"This never happens. Body replaced by stub.");
return @"Whatever. Doesn't matter.";
}
- (NSString *)stringForTypedef:(TypedefString *)string
{
NSAssert(NO, @"This never happens. Body replaced by stub.");
return @"Whatever. Doesn't matter.";
}
@end
@interface StubWithTypedefFailureDemoTests : XCTestCase
@property (strong, nonatomic) id demo;
@property (strong, nonatomic) id actualParameter;
@property (strong, nonatomic) id expectedReturn;
@end
@implementation StubWithTypedefFailureDemoTests
- (void)setUp
{
[super setUp];
self.demo = OCMClassMock([StubWithTypedefFailureDemo class]);
self.actualParameter = @"actualParameter";
self.expectedReturn = @"expectedReturn";
}
- (void)testStringForStringStubWithOCMArgAnyParameter_PASSES
{
[[[self.demo stub] andReturn:self.expectedReturn] stringForString:[OCMArg any]];
id actualReturn = [self.demo stringForString:self.actualParameter];
XCTAssertEqualObjects(actualReturn, self.expectedReturn, @"actualReturn %@ differs from expectedReturn %@", actualReturn, self.expectedReturn);
}
- (void)testStringForTypedefStubWithOCMArgAnyParameter_FAILS
{
[[[self.demo stub] andReturn:self.expectedReturn] stringForTypedef:[OCMArg any]];
id actualReturn = [self.demo stringForTypedef:self.actualParameter];
// This fails! actualReturn is nil here.
XCTAssertEqualObjects(actualReturn, self.expectedReturn, @"actualReturn %@ differs from expectedReturn %@", actualReturn, self.expectedReturn);
}
- (void)testStringForTypedefStubWithActualParameter_PASSES
{
[[[self.demo stub] andReturn:self.expectedReturn] stringForTypedef:self.actualParameter];
id actualReturn = [self.demo stringForTypedef:self.actualParameter];
XCTAssertEqualObjects(actualReturn, self.expectedReturn, @"actualReturn %@ differs from expectedReturn %@", actualReturn, self.expectedReturn);
}
@end
My project includes an optional framework, so I need to check for its availability.
This is how I do it:
- (BOOL)availabilityCheck {
return (SomeClass.class != nil);
}
So for Unit Testing I want to stub the class
method, and return nil
.
OCMockObject *mock = [OCMockObject mockForClass:SomeClass.class];
[[[[mock stub] classMethod] andReturn:nil] class];
expect(someOtherObject.availabilityCheck).to.equal(NO);
The test fails, and a NSStringFromClass
still returns the correct class name.
For some reason, the class
method can't be stubbed.
Why is that?
This issue was raised here: http://stackoverflow.com/q/26592513/449161
The implementation of protocol mock doesn't allow for the mocking of protocol class methods. It's appears to be a straightforward fix:
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
struct objc_method_description methodDescription = protocol_getMethodDescription(mockedProtocol, aSelector, YES, YES);
if(methodDescription.name == NULL)
{
methodDescription = protocol_getMethodDescription(mockedProtocol, aSelector, NO, YES);
}
// For required class methods:
if (methodDescription.name == NULL)
{
methodDescription = protocol_getMethodDescription(mockedProtocol, aSelector, YES, NO);
}
// For optional class methods
if (methodDescription.name == NULL)
{
methodDescription = protocol_getMethodDescription(mockedProtocol, aSelector, NO, NO);
}
if(methodDescription.name == NULL)
{
return nil;
}
I built this locally and didn't see it break any tests. It worked in my test though I didn't write formal tests for it.
Here is a simple test case. Class I want to test has following interface / implementation:
@interface Calculator : NSObject
- (NSNumber *)methodA;
- (NSNumber *)methodB;
@end
@implementation Calculator
- (NSNumber *)methodA {
return [self methodB];
}
- (NSNumber *)methodB {
return @0;
}
@end
And here is a test method for the partial mock:
- (void)testCalculator {
Calculator *originalObject = [[Calculator alloc] init];
Calculator *partialMock = OCMPartialMock(originalObject);
[OCMStub([partialMock methodB]) andReturn:@42];
XCTAssertEqualObjects([partialMock methodA], @42, @"Expecting 42");
XCTAssertEqualObjects([originalObject methodA], @0, @"Expecting 0");
}
The test fails on the last assertion with message
(([originalObject methodA]) equal to (@0)) failed: ("42") is not equal to ("0") - Expecting 0
It seems like a simple enough test, maybe I'm doing something wrong?
Are there any reasons why the tests run so much slower on 3 than on 2? Is there something we can do to speed things up?
It is painfully slow how. If I had to guess, 2-3x slower.
I am trying to mock static method in AFDownloadRequestOperation class (which is in Pods target)
id requestOperation = [OCMockObject mockForClass:[AFDownloadRequestOperation class]];
[[[[requestOperation stub] classMethod]andReturn:@"/Volumes/test5M"] cacheFolder];
In test file when I am trying to invoke this method, result is predictably correct, but when I am invoking this method in project target it turns out that method cacheFolder doesn't return @"/Volumes/test5M".
Is there any known issue that OCMock can't stub class methods in Pods target?
http://ocmock.org/download/ has a MIT-like license, which is, however, fully proprietary stating "All rights reserved". Can you clarify if you intended to go with an approved open source license or whether you intend to provide commercial licenses when somebody wants to develop a product using this library?
Previously I used OCMock to test the openURL:
method on [UIApplication sharedApplication]
for my login tests.
Since updating to OCMock 3, It errors every time:
[AppDelegate ocmock_replaced__controlTouchBegan:withEvent:]: unrecognized selector sent to instance 0xc02af20]
this is triggered from inside OCPartialMockObject.m
, in the method:
- (id)forwardInvocationForRealObject:(NSInvocation *)anInvocation
Seems possible that the partial mock isn't properly swizzling?
Tagged pointers are utilized for NSDate
, NSNumber
, NSIndexPath
, and other objects containing small amounts of data on 64-bit iOS devices.
A tagged pointer is a memory optimization. They are fake pointers that have the real data of the object shoved into them, if it's small enough to fit in the bytes of the pointer.
Unfortunately, this also seems to mean that a lot of objective-c runtime methods don't work, including objc_setAssociatedObject
, which throws an EXC_BAD_ACCESS
. If you check out this branch and run the OCMockLib
tests on an iPhone Retina (4-inch 64-bit), you'll see what I'm talking about.
I'm not really sure how to solve this. The implementation of a tagged pointer seems to be, in a lot of ways, at the discretion of the class itself, so it's hard to say how these objects (such as __NSTaggedDate
) are being encoded into the pointer. It'd be nice if we could turn them back into normal objects and pointers to those objects, but it seems difficult.
I don't see any information online about disabling the use of tagged pointers on 64-bit, but that would also be an solution if it were possible.
In migrating to OCMock 3, I am running into problems with ‑reject
on OCMockObject
. The places I used it with OCMock 2 now report false negatives with v3. I have a simple reproduction that shows the issue.
Is there a new recommended practice for disallowing method invocations with OCMock 3?
I encountered an unexpected problem where simply creating a partial mock of an instance of UITextField
contained within an other object results in this error if ‑resignFirstResponder
is sent to the UITextField
object:
-[OCMockTextFieldTest testCreateMock] failed: -[UITextInputTraits resignFirstResponder]: unrecognized selector sent to instance 0x102b05ba0
That was produced by running the test class I created to show the issue. I don't know the significance of this, but if the test method is written as follows, no error occurs:
- (void)testCreateMock
{
UITextField *const textField = [UITextField new];
id mockObject = OCMPartialMock(textField);
[textField resignFirstResponder];
}
The code base I work on includes several classes that utilize selector forwarding. Each forwarding class has the following code:
- (BOOL)respondsToSelector:(SEL)aSelector
{
return [super respondsToSelector:aSelector] || [self.otherObject respondsToSelector:aSelector];
}
- (id)forwardingTargetForSelector:(SEL)aSelector
{
if ([self.otherObject respondsToSelector:aSelector])
{
return self.otherObject;
}
else
{
return [super forwardingTargetForSelector:aSelector];
}
}
When migrating to OCMock 3 from version 2, I found that infinite recursion would occur if I simply created a partial mock for an instance of one of these classes. I worked around it by changing the first line of ‑forwardingTargetForSelector:
to be the following:
if ([_otherObject respondsToSelector:aSelector])
I made a simple test class that demonstrates the problem.
Hello,
OCMock v3 separated public headers from the private ones. Previously, NSInvocation+OCMAdditions.h
was available and we used it to get blocks passed into the mock objects easily.
It isn't available anymore, is there a particular reason why?
Here's usage example: http://stackoverflow.com/a/20694495/1169136. Maybe we were using it incorrectly in the first place and there's another way to do this?
I'm getting an error when trying to expect a call on my mock, created from a protocol.
I'm trying to check that
@protocol MyProtocol
- (void)viewModel:viewModel didReceiveSearchError:(NSError *)error;
@end
- (void)testOCMock
{
id mock = [OCMockObject mockForProtocol:@protocol(MyProtocol)];
[[mock expect] viewModel:self.viewModel didReceiveSearchError:[OCMArg any]];
}
I get this error:
OCMockObject[MyProtocol]: cannot stub or expect method 'viewModel:didReceiveSearchError:' because no such method exists in the mocked class.
If I remove the first parameter viewModel out of the function, everything works properly.
Thanks guys, great project!
Setup:
We stub a class method in a XCTest
method:
- (void) testStub {
id mock = [OCMock mockForClass:[SomeTestClass class]];
[[[mock stub] andReturn:@"Test"] someClassMethod];
SomeViewController *viewController = [[SomeViewController] alloc] init];
[viewController viewDidLoad];
XCTAssertEqualObjects(view.someString, @"Test");
}
and in viewDidLoad
the class method someClassMethod
is called and set to someString
This works fine.
But if we move SomeTestClass
to an CocoaPod Library and integrate it to the project with CocoaPods the class method stubing doesn't work.
We made a little example project for this issue https://github.com/x2on/OCMock-Test-Project
Any idea why this happens?
I've been using OCMock 2 for many months along side Specta without issue. Recently I upgraded to 3 and began using the OCMVerify macro for verify-after-running. In certain instances I am receiving EXC_BAD_ACCESS when the invocation matcher is matching arguments, specifically if(([recordedArg isEqual:passedArg] == NO)
. It seems the the passedArg is released by ARC at some point and by the time it is pulled out of the invocation it is gone.
I'm not 100% where the issue lies and I can't seem to be able to replicate this is a sample project to provide as an example for this issue. It seems to happen more on iOS 64bit simulator than 32bit, but it isn't everytime.
The layout of my tests look something like this:
SpecBegin(Spec)
describe(@"Spec", ^{
__block TestSubject *subject;
__block id mockObject;
beforeAll(^{
mockObject = OCMClassMock([MyObject class]);
subject = [[TestSubject alloc] initWithObject:mockObject];
});
describe(@"when something happens", ^{
beforeAll(^{
MyInvocation successfulInvocation = [ArrangeInvocation successfulInvocation];
OCMStub([mockObject anotherMethod]);
.andDo(successfulInvocation);
[subject doSomething];
});
it(@"does something", ^{
OCMVerify([mockObject methodCalledWithParam:[SomeParam param]
completionHandler:OCMOCK_ANY]);
});
});
});
SpecEnd
I have been able to work around this issue by moving the expectation to before the tests run but ideally would like to use the new features of OCMock 3. I'm just not sure where to start diagnosing this and as I said can't seem to replicate the issue outside of my main codebase.
I'm curious why there isn't an OCMReject
to match the OCMExpect
. We have to revert to the old syntax for rejects.
Hi there!
I'm using OCMock with a test framework called Cedar(https://github.com/pivotal/cedar). It is running tests in a separate target, that is not explicitly marked as "test" target by XCode. There's no injection in main target, it is just a separate target with a separate application delegate("CedarApplicationDelegate"). After launching app crashes on runtime with EXC_BREAKPOINT.
Console log:
dyld: Library not loaded: @rpath/XCTest.framework/XCTest
Reverting OCMock to 2.2 fixes the issue, since it is not dependent on XCTest.framework.
P.S. Using XCode 6.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.