/*----------------------------------------------------*/ #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]; }