System Test Post-" Release Requirements 1 3 5-10 10 10-100 Architecture - 1 10 15 25-100 Coding - - 1 10 10-25 Cost of Fixing Bugs Found at Different Stages of the Software Development Process
talks to the database" • It communicates across the network" • It touches the file system" • It can't run at the same time as any of your other unit tests" • You have to do special things to your environment (such as editing config files) to run it.
automated! • Tests a single logical concept in the system" • Consistently returns the same result" • Maintainable and order-independent" • Independent! • Runs fast" • Readable! • Trustworthy
automatically include a unit testing target" • Test classes do not have a header file" • It’s not necessary to add application’s source files to the XCTest Target" • Each test case is a method with prefix test" • Xcode 5 allows to run tests from the editor
setUp]; // This method is called before the invocation of each test method in the class. } - (void)tearDown { // This method is called after the invocation of each test method in the class. [super tearDown]; } - (void)testExample { XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); }
up as necessary. " • Act on an object. " • Assert that something is as expected. - (void)testMakeConversionWithModeMilesToKilometers { // Arrange sut.value = @(1); // Act [sut makeConversionWithMode:ConverterModeMilesToKm]; // Assert XCTAssertTrue([sut.text isEqualToString:@"1.609344"], @"should convert from miles to kilometers"); }
collaborators of the system under test so that the test is independent of the implementation of the collaborators. Mock Object SUT Behavior Verification Setup Exercise Verify Tear Down Behavior " (Indirect" Outputs) Fake Verify
fake.expectedName = kSomeNotification; sut.notificationCenter = (id)fake; // Assert [fake verify]; } -[ConverterViewControllerTests testViewDidLoadAddsObserver] failed: expected method was not called with specified parameters How can we use it?
as if it were an instance of // SomeClass. id mock = [OCMockObject mockForClass:[SomeClass class]]; // Tells the mock object that someMethod: should be called with an argument // that is equal to someArgument. [[mock expect] someMethod:someArgument]; // After this setup the functionality under test should be invoked // followed by [mock verify]; // When a method is called on a mock object that has not been set up with // either expect or stub the mock object will raise an exception. This // fail-fast mode can be turned off by creating a "nice" mock: id mock = [OCMockObject niceMockForClass:[SomeClass class]]; // While nice mocks will simply ignore all unexpected methods it is // possible to disallow specific methods: [[mock reject] someMethod];
called with // someArgument it should return aValue. [[[mock stub] andReturn:aValue] someMethod:someArgument]; // It is not possible to pass primitive types directly. [[[mock stub] andReturnValue:@YES] aMethodReturnABoolean:someArgument]; // The mock object can also throw an exception or post a notification // when a method is called [[[mock stub] andThrow:anException] someMethod:someArgument]; [[[mock stub] andPost:aNotification] someMethod:someArgument]; // If Objective-C blocks are available a block can be used to handle the // invocation and set up a return value void (^theBlock)(NSInvocation *) = ^(NSInvocation *invocation) { /* code that reads and modifies the invocation object */ }; [[[mock stub] andDo:theBlock] someMethod:[OCMArg any]];
team must write and run tests" • Start with warnings and bugs! • Isolate modules and classes! • Don’t miss refactoring! • Increase test-coverage step-by-step with new functionality