-
-
Save oks/a32688f31b092388b3e8 to your computer and use it in GitHub Desktop.
OCMock cheatsheet
This file contains hidden or 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 - Quick | |
expect(x)).to(equal(y)); | |
expectAction(^{ [exception raise]; }).to(raiseException()); // no return value expectation | |
// Passes if actual is equivalent to expected: | |
expect(actual).to(equal(expected)) | |
// Passes if actual is not equivalent to expected: | |
expect(actual).toNot(equal(expected)) | |
// Passes if actual has the same pointer address as expected: | |
expect(actual).to(beIdenticalTo(expected)); | |
// Passes if actual does not have the same pointer address as expected: | |
expect(actual).toNot(beIdenticalTo(expected)); | |
expect(actual).to(beLessThan(expected)); | |
expect(actual).to(beLessThanOrEqualTo(expected)); | |
expect(actual).to(beGreaterThan(expected)); | |
expect(actual).to(beGreaterThanOrEqualTo(expected)); | |
expect(actual).to(beCloseTo(expected).within(delta)); // expect(@(10.01)).to(beCloseTo(@10).within(0.1)); | |
//beAnInstanceOf uses the -[NSObject isMemberOfClass:] method to test membership. beAKindOf uses -[NSObject isKindOfClass:]. | |
// Passes if instance is an instance of aClass: | |
expect(instance).to(beAnInstanceOf(aClass)); | |
// Passes if instance is an instance of aClass or any of its subclasses: | |
expect(instance).to(beAKindOf(aClass)); | |
// Passes if actual is not nil, true, or an object with a boolean value of true: | |
expect(actual).to(beTruthy()); | |
// Passes if actual is only true (not nil or an object conforming to BooleanType true): | |
expect(actual).to(beTrue()); | |
// Passes if actual is nil, false, or an object with a boolean value of false: | |
expect(actual).to(beFalsy()); | |
// Passes if actual is only false (not nil or an object conforming to BooleanType false): | |
expect(actual).to(beFalse()); | |
// Passes if actual is nil: | |
expect(actual).to(beNil()); | |
// Passes if actual, when evaluated, raises an exception: | |
expect(actual).to(raiseException()) | |
// Passes if actual raises an exception with the given name | |
expect(actual).to(raiseException().named(name)) | |
// Passes if actual raises an exception with the given name and reason: | |
expect(actual).to(raiseException().named(name).reason(reason)) | |
// Passes if actual raises an exception and it passes expectations in the block | |
// (in this case, if name begins with 'a r') | |
expect(actual).to(raiseException().satisfyingBlock(^(NSException *exception) { | |
expect(exception.name).to(beginWith(@"a r")); | |
})); | |
// Passes if expected is a member of actual: | |
expect(actual).to(contain(expected)); //expect(@[@"whale", @"dolphin", @"starfish"]).to(contain(@"dolphin")); | |
// Passes if actual is an empty collection (it contains no elements): | |
expect(actual).to(beEmpty()); | |
// Passes if the elements in expected appear at the beginning of actual: | |
expect(actual).to(beginWith(expected)); | |
// Passes if the the elements in expected come at the end of actual: | |
expect(actual).to(endWith(expected)); | |
// Passes if actual contains substring expected: | |
expect(actual).to(contain(expected)); | |
// Passes if actual begins with substring: | |
expect(actual).to(beginWith(expected)); | |
// Passes if actual ends with substring: | |
expect(actual).to(endWith(expected)); | |
// Passes if actual is an empty string, "": | |
expect(actual).to(beEmpty()); | |
// Passes if actual matches the regular expression defined in expected: | |
expect(actual).to(match(expected)) | |
expect(@[@1, @2, @3,@4]).to(allPass(beLessThan(@5))); | |
// passes if actual collection's count is equal to expected | |
expect(actual).to(haveCount(expected)) | |
// passes if actual collection's count is not equal to expected | |
expect(actual).notTo(haveCount(expected)) | |
// passes if actual is either less than 10 or greater than 20 | |
expect(actual).to(satisfyAnyOf(beLessThan(@10), beGreaterThan(@20))) | |
// can include any number of matchers -- the following will pass | |
// **be careful** -- too many matchers can be the sign of an unfocused test | |
expect(@6).to(satisfyAnyOf(equal(@2), equal(@3), equal(@4), equal(@5), equal(@6), equal(@7))) | |
waitUntil(^(void (^done)(void)){ | |
// do some stuff that takes a while... | |
[NSThread sleepForTimeInterval:0.5]; | |
done(); | |
}); | |
waitUntilTimeout(10, ^(void (^done)(void)){ | |
// do some stuff that takes a while... | |
[NSThread sleepForTimeInterval:1]; | |
done(); | |
}); | |
//Quick | |
beforeSuite(^{ | |
[OceanDatabase createDatabase:@"test.db"]; | |
[OceanDatabase connectToDatabase:@"test.db"]; | |
}); | |
afterSuite(^{ | |
[OceanDatabase teardownDatabase:@"test.db"]; | |
}); | |
beforeEachWithMetadata(^(ExampleMetadata *exampleMetadata){ | |
NSLog(@"Example number %l is about to be run.", (long)exampleMetadata.exampleIndex); | |
}); | |
afterEachWithMetadata(^(ExampleMetadata *exampleMetadata){ | |
NSLog(@"Example number %l has run.", (long)exampleMetadata.exampleIndex); | |
}); | |
describe(@"a dolphin", ^{ | |
__block Dolphin *dolphin = nil; | |
beforeEach(^{ dolphin = [Dolphin new]; }); | |
describe(@"its click", ^{ | |
context(@"when the dolphin is not near anything interesting", ^{ | |
it(@"is only emitted once", ^{ | |
expect(@([[dolphin click] count])).to(equal(@1)); | |
}); | |
}); | |
context(@"when the dolphin is near something interesting", ^{ | |
beforeEach(^{ | |
[[Jamaica dolphinCove] add:[SunkenShip new]]; | |
[[Jamaica dolphinCove] add:dolphin]; | |
}); | |
it(@"is emitted three times", ^{ | |
expect(@([[dolphin click] count])).to(equal(@3)); | |
}); | |
}); | |
}); | |
}); | |
QuickConfigurationBegin(EdibleSharedExamplesConfiguration) | |
+ (void)configure:(Configuration *configuration) { | |
sharedExamples(@"something edible", ^(QCKDSLSharedExampleContext exampleContext) { | |
it(@"makes dolphins happy") { | |
Dolphin *dolphin = [[Dolphin alloc] init]; | |
dolphin.happy = NO; | |
id<Edible> edible = exampleContext()[@"edible"]; | |
[dolphin eat:edible]; | |
expect(dolphin.isHappy).to(beTruthy()) | |
} | |
}); | |
} | |
QuickConfigurationEnd | |
QuickSpecBegin(MackerelSpec) | |
__block Mackerel *mackerel = nil; | |
beforeEach(^{ | |
mackerel = [[Mackerel alloc] init]; | |
}); | |
itBehavesLike(@"someting edible", ^{ return @{ @"edible": mackerel }; }); | |
QuickSpecEnd | |
QuickSpecBegin(CodSpec) | |
__block Mackerel *cod = nil; | |
beforeEach(^{ | |
cod = [[Cod alloc] init]; | |
}); | |
itBehavesLike(@"someting edible", ^{ return @{ @"edible": cod }; }); | |
QuickSpecEnd | |
/*----------------------------------------------------*/ | |
#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