Upgrade to Pro — share decks privately, control downloads, hide ads and more …

iOS Unit Testing - View Controller challenge

iOS Unit Testing - View Controller challenge

Ciklum Mobile Saturday in Kharkiv

Avatar for Maksym Koshtenko

Maksym Koshtenko

October 25, 2014

More Decks by Maksym Koshtenko

Other Decks in Programming

Transcript

  1. UIViewController Model View Controller! update notify actions update <Protocol> <Protocol>

    Controller Controller View present present update TableView Facade MapView delegate delegate update Notification! Center Blocks IBOutlets IBActions Notifications API Database Notifications
  2. is a fake object in the system that verifies the

    system under test interacted as expected with the fake object. Mock Object
  3. It is common in unit tests to mock or stub

    collaborators of the system under test so that the test is independent of the implementation of the collaborators. Mock Object Behavior Verification SUT Setup Exercise Verify Tear Down Behavior ! (Indirect! Outputs) DOC
  4. It is common in unit tests to mock or stub

    collaborators of the system under test so that the test is independent of the implementation of the collaborators. Mock Object Behavior Verification SUT Setup Exercise Verify Tear Down Behavior ! (Indirect! Outputs) Fake
  5. It is common in unit tests to mock or stub

    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
  6. Test Mocks SUT communicates with the mock object, and all

    communication is recorded in the mock. The test uses the mock to verify that the test passes. SUT Mock Communicate Assert
  7. Test Stubs Returns specified result for a message, stubs can’t

    fail the test. SUT Stub Communicate Assert
  8. - (void)setUp { [super setUp]; sut = [ConverterViewController new]; }

    - (void)tearDown { sut = nil; [super tearDown]; } Is Outlet Connected?
  9. - (void)setUp { [super setUp]; sut = [ConverterViewController new]; }

    - (void)tearDown { sut = nil; [super tearDown]; } - (void)testThatTextFieldOutletIsConnected { XCTAssertNotNil(sut.textField, @"outlet should be connected"); } Is Outlet Connected?
  10. - (void)setUp { [super setUp]; sut = [ConverterViewController new]; }

    - (void)tearDown { sut = nil; [super tearDown]; } - (void)testThatTextFieldOutletIsConnected { XCTAssertNotNil(sut.textField, @"outlet should be connected"); } Is Outlet Connected?
  11. - (void)setUp { [super setUp]; sut = [ConverterViewController new]; }

    - (void)tearDown { sut = nil; [super tearDown]; } - (void)testThatTextFieldOutletIsConnected { [sut view]; XCTAssertNotNil(sut.textField, @"outlet should be connected"); } Is Outlet Connected?
  12. - (void)testButtonActionBinding { [sut view]; NSArray* acts = [sut.button actionsForTarget:sut

    forControlEvent:UIControlEventTouchUpInside]; XCTAssert([acts containsObject:@"onButton:"], @"should use correct action"); } Is Action Connected?
  13. viewDidLoad Unit testing of a view controller nearly always means

    writing the view controller methods differently
  14. viewDidLoad - should call helper methods! - each of the

    helper methods should do just one thing (SOLID principles)! - write tests for each of the helper methods! - test viewDidLoad calls helper methods (partial mock)
  15. OCMock Provides •stub objects that return pre-determined values for specific

    method invocations! •dynamic mocks that can be used to verify interaction patterns! •partial mocks to overwrite selected methods of existing objects
  16. ///Partial mock the system under test to stub out internal

    methods with expected responses + (id)partialMockForObject:(NSObject *)anObject; id sutMock = [OCMockObject partialMockForObject:sut]; OCMock partial mock
  17. -(void)testTappingDetailsShouldDisplayDetails { UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:sut]; id mockNav

    = [OCMockObject partialMockForObject:nav]; [[mockNav expect] pushViewController:[OCMArg any] animated:YES]; [sut onShowDetailsButton:nil]; [mockNav verify]; } UINavigationController
  18. dealloc ❓hook dealloc method of SUT when setup! ❓record calling

    of the hook! ❓verify if hook is called after teardown
  19. /// Adds a block of code before/instead/after the current `selector`

    for a specific instance. - (id<AspectToken>)aspect_hookSelector:(SEL)selector withOptions:(AspectOptions)options usingBlock:(id)block error:(NSError **)error; /// Called after the original implementation (default) AspectPositionAfter, /// Will replace the original implementation. AspectPositionInstead, /// Called before the original implementation. AspectPositionBefore, Aspects
  20. dealloc ✅ hook dealloc method of SUT when setup !

    ✅ record calling of the hook! ✅ verify if hook is called after teardown Aspects instance var XCTAssert
  21. @interface ConverterViewControllerTests : XCTestCase { ConverterViewController* sut; BOOL _sutDeallocated; }

    @end - (void)setUp { [super setUp]; sut = [ConverterViewController new]; _sutDeallocated = NO; [sut aspect_hookSelector:NSSelectorFromString(@"dealloc") withOptions:AspectPositionBefore usingBlock:^(id<AspectInfo> aspectInfo){ _sutDeallocated = YES; } error:nil]; } - (void)tearDown { sut = nil; XCTAssertTrue(_sutDeallocated, @"SUT is not deallocated"); [super tearDown]; } dealloc
  22. @interface } @end - ( [ } - ( [

    } dealloc [sut aspect_hookSelector:NSSelectorFromString(@"dealloc") withOptions:AspectPositionBefore usingBlock:^(id<AspectInfo> aspectInfo){ _sutDeallocated = YES; } error:nil];
  23. Testing simple things is simple, ! and ! testing complex

    things is complex. UIViewController
  24. Choosing not to test view controllers is the decision not

    to test most of your code. UIViewController
  25. How to Test • IBOutlets & IBActions! • viewDidLoad! •

    UINavigationController! • Class Methods! • dealloc Demo Code https:/ /github.com/maksumko/ConverterApp" ! maksum.ko contact me: