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