Last active
July 30, 2024 07:14
-
-
Save kharmabum/51b52c37b6166d897826 to your computer and use it in GitHub Desktop.
OCMock cheatsheet
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*----------------------------------------------------*/ | |
#pragma mark - XCTAsserts | |
/*----------------------------------------------------*/ | |
XCTAssert(expression, format...); | |
XCTAssertTrue(expression, format...); | |
XCTAssertFalse(expression, format...); | |
XCTAssertEqual(expression1, expression2, format...); | |
XCTAssertNotEqual(expression1, expression2, format...); | |
XCTAssertNil(expression, format...); | |
XCTAssertNotNil(expression, format...); | |
XCTFail(format...); | |
/*----------------------------------------------------*/ | |
#pragma mark - Expecta matchers | |
/*----------------------------------------------------*/ | |
expect(x).to.equal(y); | |
expect(x).to.beIdenticalTo(y); | |
expect(x).to.beNil(); | |
expect(x).to.beTruthy(); | |
expect(x).to.beFalsy(); | |
expect(x).to.contain(y); | |
expect(x).to.beSupersetOf(y); | |
expect(x).to.haveCountOf(y); | |
expect(x).to.beEmpty(); | |
expect(x).to.beInstanceOf([Foo class]); | |
expect(x).to.beKindOf([Foo class]); | |
expect([Foo class]).to.beSubclassOf([Bar class]); | |
expect(x).to.beLessThan(y); | |
expect(x).to.beLessThanOrEqualTo(y); | |
expect(x).to.beGreaterThan(y); | |
expect(x).to.beGreaterThanOrEqualTo(y); | |
expect(x).to.beInTheRangeOf(y,z); | |
expect(x).to.beCloseTo(y); | |
expect(x).to.beCloseToWithin(y, z); | |
expect(^{ /* code */ }).to.raise(@"ExceptionName"); | |
expect(^{ /* code */ }).to.raiseAny(); | |
expect(x).to.conformTo(y); | |
expect(x).to.respondTo(y); | |
expect(^{ /* code */ }).to.notify(@"NotificationName"); | |
expect(^{ /* code */ }).to.notify(notification); | |
expect(x).to.beginWith(y); | |
expect(x).to.endWith(y); | |
// inverting matchers | |
expect(x).notTo.equal(y); | |
expect(x).toNot.equal(y); | |
// asynchronous testing | |
[Expecta setAsynchronousTestTimeout:x] // default is 1 sec | |
expect(x).will.beNil(); | |
expect(x).willNot.beNil(); | |
expect(x).after(3).to.beNil(); | |
expect(x).after(2.5).notTo.equal(42); | |
/*----------------------------------------------------*/ | |
#pragma mark - OCMock v3.x | |
/*----------------------------------------------------*/ | |
// default mock creation | |
id classMock = OCMClassMock([SomeClass class]); // default is nice | |
id protocolMock = OCMProtocolMock(@protocol(SomeProtocol)); // default is nice | |
// strict mock creation | |
id classMock = OCMStrictClassMock([SomeClass class]); | |
id protocolMock = OCMStrictProtocolMock(@protocol(SomeProtocol)); | |
//Partial mocks alter the class of the mocked object, though. (In fact, they create a subclass and switch the class of the mocked object to that subclass.) | |
// This means that calls using a reference to the real object, even including self in methods where the object calls itself, are also affected by stubs and expectations. | |
id partialMock = OCMPartialMock(anObject); | |
OCMStub([mock someMethod]).andReturn(anObject); | |
OCMStub([mock aMethodReturningABoolean]).andReturn(YES); | |
OCMStub([mock someMethod]).andThrow(anException); | |
OCMStub([mock someMethod]).andPost(aNotification); | |
OCMStub([mock someMethod]).andPost(aNotification).andReturn(aValue); | |
OCMStub([mock someMethod]).andForwardToRealObject(); // only useful when chaining actions or when using expectations. | |
OCMStub([mock someMethod]).andDo(nil); // supress behavior. only useful with partial mocks or when mocking class methods | |
// delegatign to another method (aka swizzling) | |
// method signatures must match | |
// Arguments will be passed, and the return value of the replacement method is returned from the stubbed method. | |
// It is common to implement the replacement method in the test case itself. | |
OCMStub([mock someMethod]).andCall(anotherObject, @selector(aDifferentMethod)); | |
OCMStub([partialMock someMethod]).andCall(anotherObject, @selector(aDifferentMethod)); // per instance basis | |
// delegating to a block | |
OCMStub([mock someMethod]).andDo(^(NSInvocation *invocation) | |
{ /* block that handles the method invocation */ }); | |
// pas by reference arguments | |
OCMStub([mock someMethodWithReferenceArgument:[OCMArg setTo:anObject]]); | |
OCMStub([mock someMethodWithReferenceArgument:[OCMArg setToValue:OCMOCK_VALUE((int){aValue})]]); | |
// Verifying interactions | |
// | |
// There is no need to insure that a reference to the mock is used, calls can be made using references to the real object. | |
// If the method has not been invoked an error is reported. | |
OCMVerify([anObject someMethod]); // OR | |
OCMVerify([mock someMethod]); | |
// Strict mocks and expectations | |
// | |
// If an expected method has not been invoked, or has not been invoked with the right arguments, then an error is reported | |
id classMock = OCMClassMock([SomeClass class]); | |
OCMExpect([classMock someMethodWithArgument:[OCMArg isNotNil]]); | |
OCMExpect([classMock someMethod]).andReturn(@"a string for testing"); | |
/* run code under test, which is assumed to call someMethod */ | |
OCMVerifyAll(classMock); | |
OCMVerifyAllWithDelay(mock, aDelay); | |
// Expectation order | |
[mock setExpectationOrderMatters:YES]; | |
OCMExpect([mock someMethod]); | |
OCMExpect([mock anotherMethod]); | |
/* calling anotherMethod before someMethod will cause an exception to be thrown */ | |
[mock anotherMethod]; | |
// Nice mocks and rejections | |
// | |
// Regular mock objects simply return the default value for the return type for | |
// methods that haven't been set up with stub/expect but can be configured on | |
// a per-method basis to fail fast: | |
id mock = OCMClassMock([SomeClass class]); | |
[[mock reject] someMethod]; | |
OCMVerifyAll(mock); | |
// Argument constraints | |
OCMStub([mock someMethodWithAnArgument:[OCMArg any]]) ; | |
OCMStub([mock someMethodWithPointerArgument:[OCMArg anyPointer]]); | |
OCMStub([mock someMethodWithSelectorArgument:[OCMArg anySelector]]); | |
[[[mock stub] ignoringNonObjectArgs] someMethodWithIntArgument:0]; // to ignore args that are not objects, pointers, selectors | |
// Matching arguments | |
OCMStub([mock someMethod:aValue]); | |
OCMStub([mock someMethod:[OCMArg isNil]]); | |
OCMStub([mock someMethod:[OCMArg isNotNil]]); | |
OCMStub([mock someMethod:[OCMArg isNotEqual:aValue]]); | |
OCMStub([mock someMethod:[OCMArg isKindOfClass:[SomeClass class]]]); | |
OCMStub([mock someMethod:[OCMArg checkWithSelector:aSelector onObject:anObject]]); | |
OCMStub([mock someMethod:[OCMArg checkWithBlock:^BOOL(id value) { /* return YES if value is ok */ }]]); | |
// Stubbing class methods is generally done by the same means. | |
// in the event an instance and class method share the same name... | |
id classMock = OCMClassMock([SomeClass class]); | |
OCMStub(ClassMethod([classMock ambiguousMethod])).andReturn(@"Test string"); | |
// restores a class - removes stub implementations | |
// This is only necessary if the original state must be restored before the end of the test. | |
// The mock automatically calls stopMocking during its own deallocation. | |
[classMock stopMocking]; | |
// observer mocks - strict by default | |
id observerMock = OCMObserverMock(); | |
[notificatonCenter addMockObserver:observerMock name:SomeNotification object:nil]; | |
[[observerMock expect] notificationWithName:SomeNotification object:[OCMArg any]]; | |
OCMVerifyAll(observerMock); | |
// Notes: | |
// * Only one mock at a time can stub class methods on a given class otherwise behavior is undefined | |
// * If the mock object that added a stubbed class method is not deallocated then the stubbed method will persist, even across tests | |
// * Always set up `stub` after `expect` (or just do or the other) | |
// * It is not possible to create partial mocks for instances of toll-free bridged class, e.g. NSString, or for objects represented with tagged pointers, e.g. NSDate on some architectures. | |
// * It is not possible to mock certain core runtime methods including class, methodSignatureForSelector:, and forwardInvocation: | |
// * It is not possible to stub or verify class methods on NSString. | |
// * It is not possible use verify-after-running with methods implemented in NSObject or a category on it. It is possible to use verify-after-running when the method is overriden in a subclass. | |
// * It is not possible use verify-after-running with private methods in core Apple classes (all methods with an underscore prefix and/or suffix in a class with either NS or UI as prefix). | |
// * It is currently not possible to verify a method with a delay. This is currently only possible using the expect-run-verify approach | |
/*----------------------------------------------------*/ | |
#pragma mark - OCMock v2.x | |
/*----------------------------------------------------*/ | |
// Making mocks | |
// | |
// Factory Method Description | |
// +mockForClass: Create a mock based on the given class | |
// +mockForProtocol: Create a mock based on the given protocol | |
// +niceMockForClass: Create a "nice" mock based on the given class | |
// +niceMockForProtocol: Create a "nice" mock based on the given protocol | |
// +partialMockForObject: Create a mock based on the given object | |
// +observerMock: Create a notification observer (more on this later) | |
// Return values | |
// | |
// Method Explanation | |
// -andReturn: Return the given object | |
// -andReturnValue: Return a non-object value (wrapped in a NSValue) | |
// -andThrow: Throw the given exception | |
// -andPost: Post the given notification | |
// -andCall:onObject: Call the selector on the given object | |
// -andDo: Invoke the given block (only on OS X 10.6 or iOS 4) | |
// Args | |
// | |
// OCMArg method Description | |
// +any Any argument is accepted. | |
// +anyPointer Accepts any pointer | |
// +isNil The given argument must be nil | |
// +isNotNil The given argument must not be nil | |
// +isNotEqual: Given argument is not object-equivalent with expectation | |
// +checkWithSelector:onObject: Check the argument with the given action/target pair | |
// +checkWithBlock: Check the argument with the given block (OS X 10.6 or iOS 4) | |
id mockThing = [OCMock mockForClass[Thing class]]; | |
Thing *someThing = [Thing alloc] init]; | |
id aMock = [OCMockObject partialMockForObject:someThing] | |
/*----------------------------------------------------*/ | |
#pragma mark - Callbacks | |
/*----------------------------------------------------*/ | |
// - (void)downloadWeatherDataForZip:(NSString *)zip | |
// callback:(void (^)(NSDictionary *response))callback; | |
- (void)testCallbackHandling | |
{ | |
Thing *someThing = [Thing alloc] init]; | |
id aMock = [OCMockObject partialMockForObject:someThing] | |
[[[aMock stub] andDo:^(NSInvocation *invoke) { | |
//2. declare a block with same signature | |
void (^weatherStubResponse)(NSDictionary *dict); | |
//3. link argument 3 with with our block callback | |
[invoke getArgument:&weatherStubResponse atIndex:3]; | |
//4. invoke block with pre-defined input | |
NSDictionary *testResponse = @{@"high": 43 , @"low": 12}; | |
weatherStubResponse(groupMemberMock); | |
}] downloadWeatherDataForZip@"80304" callback:[OCMArg any] ]; | |
} | |
/*----------------------------------------------------*/ | |
#pragma mark - Singleton testing | |
/*----------------------------------------------------*/ | |
- (void)testOpenUrl | |
{ | |
ViewController *toTest = [[ViewController alloc] init]; | |
NSURL *toOpen = [NSURL URLWithString:@"http://www.google.com"]; | |
// Create a partial mock of UIApplication | |
id mockApplication = [OCMockObject partialMockForObject:[UIApplication sharedApplication]]; | |
// Set an expectation that the UIApplication will be told to open the url | |
[[mockApplication expect] openURL:toOpen]; | |
// Even though the method is invoked on the 'real' singleton ref and not the mock, the mock still handles it | |
// and can be used to verify | |
[toTest launchURL:toOpen]; | |
[mockApplication verify]; | |
[mockApplication stopMocking]; | |
} | |
/*----------------------------------------------------*/ | |
#pragma mark - Exposing private data/methods w/ category | |
/*----------------------------------------------------*/ | |
#import "SomeObject.h" | |
// Definition of the category Test on the class SomeObject | |
@interface SomeObject (Test) | |
- (void)aPrivateMethod; | |
@end | |
@interface SomeObjectTests : XCTestCase | |
@property (nonatomic, strong) SomeObject *toTest; | |
@end | |
@implementation SomeObjectTests | |
- (void)setUp | |
{ | |
[super setUp]; | |
self.toTest = [[SomeObject alloc] init]; | |
} | |
- (void)testPrivateMethod | |
{ | |
[self.toTest aPrivateMethod]; | |
} | |
@end | |
/*----------------------------------------------------*/ | |
#pragma mark - Mocking protocol | |
/*----------------------------------------------------*/ | |
- (void)testInit { | |
id mockService = [OCMockObject mockForProtocol:@protocol(AVQuoteService)]; | |
[[mockService expect] initiateConnection]; | |
AVStockPortfolio *portfolio = [[AVStockPortfolio alloc] initWithService:mockService]; | |
[mockService verify]; | |
} | |
/*----------------------------------------------------*/ | |
#pragma mark - Verify notification observed | |
/*----------------------------------------------------*/ | |
- (void)testSellSharesInStock { | |
id mock = [OCMockObject observerMock]; | |
// OCMock adds a custom methods to NSNotificationCenter via a category | |
[[NSNotificationCenter defaultCenter] addMockObserver:mock | |
name:AVStockSoldNotification | |
object:nil]; | |
[[mock expect] notificationWithName:AVStockSoldNotification object:[OCMArg any]]; | |
AVPortfolio *portfolio = [self createPortfolio]; // made-up factory method | |
[portfolio sellShares:100 inStock:@"AAPL"]; | |
[mock verify]; | |
} | |
/*----------------------------------------------------*/ | |
#pragma mark - Post notifications with stubs | |
/*----------------------------------------------------*/ | |
- (void)testNotification | |
{ | |
NSNotification *notfication = [NSNotification notificationWithName:@"foo" object:nil]; | |
[[[mock expect] andPost:notfication] andReturn:@"FOOBAR"] doSomethingMagical]; | |
} | |
/*----------------------------------------------------*/ | |
#pragma mark - Validate args, general mucking around | |
/*----------------------------------------------------*/ | |
- (void)testSellSharesInStock { | |
id quoteService = [[OCMockObject] mockForProtocol:@protocol(AVQuoteService)]; | |
[[[quoteService expect] andDo:^(NSInvocation *invocation) { | |
// validate arguments, set return value on the invocation object | |
}] priceForStock:@"AAPL"]; | |
AVStockPortfolio *portfolio = [[AVStockPortfolio alloc] initWithService:quoteService]; | |
[portfolio sellShares:100 inStock:@"AAPL"]; | |
// other validations and assertions | |
[quoteService verify]; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thank you! Very useful