Slide 1

Slide 1 text

Testing in iOS a hastily assembled presentation by bradley grzesiak

Slide 2

Slide 2 text

Frameworks • SenTest • OCHamcrest • Kiwi • Frank • UIAutomation • KIF

Slide 3

Slide 3 text

SenTest • The default • Two levels • File • - (void)test* • meh

Slide 4

Slide 4 text

Test Suite 'All tests' started at 2012-02-20 17:05:44 +0000 Test Suite '/Users/.../ThrowawayTests.octest(Tests)' started at 2012-02-20 17:05:44 +0000 Test Suite 'ThrowawayTests' started at 2012-02-20 17:05:44 +0000 Test Case '-[ThrowawayTests testExample]' started. Test Case '-[ThrowawayTests testExample]' passed (0.000 seconds). Test Suite 'ThrowawayTests' finished at 2012-02-20 17:05:44 +0000. Executed 1 test, with 0 failures (0 unexpected) in 0.000 (0.000) seconds Test Suite '/Users/.../ThrowawayTests.octest(Tests)' finished at 2012-02-20 17:05:44 +0000. Executed 1 test, with 0 failures (0 unexpected) in 0.000 (0.000) seconds Test Suite 'All tests' finished at 2012-02-20 17:05:44 +0000. Executed 1 test, with 0 failures (0 unexpected) in 0.000 (0.010) seconds STAssertEqualObjects(@”a”, @”a”, @”my description”);

Slide 5

Slide 5 text

Test Suite 'All tests' started at 2012-02-20 17:02:01 +0000 Test Suite '/Users/.../ThrowawayTests.octest(Tests)' started at 2012-02-20 17:02:01 +0000 Test Suite 'ThrowawayTests' started at 2012-02-20 17:02:01 +0000 Test Case '-[ThrowawayTests testExample]' started. /.../ThrowawayTests.m:29: error: -[ThrowawayTests testExample] : 'a' should be equal to 'b' my description Test Case '-[ThrowawayTests testExample]' failed (0.000 seconds). Test Suite 'ThrowawayTests' finished at 2012-02-20 17:02:01 +0000. Executed 1 test, with 1 failure (0 unexpected) in 0.000 (0.000) seconds Test Suite '/Users/.../ThrowawayTests.octest(Tests)' finished at 2012-02-20 17:02:01 +0000. Executed 1 test, with 1 failure (0 unexpected) in 0.000 (0.000) seconds Test Suite 'All tests' finished at 2012-02-20 17:02:01 +0000. Executed 1 test, with 1 failure (0 unexpected) in 0.000 (0.002) seconds STAssertEqualObjects(@”a”, @”b”, @”my description”);

Slide 6

Slide 6 text

OCHamcrest • matchers! • assertThat(foo, equalTo(bar) • poly-framework, polyglot

Slide 7

Slide 7 text

Kiwi • Like RSpec • describe, context, it, xit, etc. • remember the __block modifier! • infinitely nestable

Slide 8

Slide 8 text

Kiwi test file - top #import "Kiwi.h" #import "NSDictionary+BWTravisCI.h"

Slide 9

Slide 9 text

Kiwi test file - surround SPEC_BEGIN(NSDictionarySpec) ... SPEC_END

Slide 10

Slide 10 text

Kiwi test file - top-level describe(@"NSDictionary+BWTravisCI", ^{ __block NSDictionary *subject = nil; beforeEach(^{ subject = [NSDictionary dictionaryWithObjectsAndKeys:@"1", @"a", @"2", @"b", @"3", @"c", nil]; }); describe(@"subdictionaryUsingKeys", ^{ ... }); });

Slide 11

Slide 11 text

Kiwi test file - contexts & tests context(@"matching nothing", ^{ it(@"returns an empty dictionary", ^{ NSDictionary *result = [subject subdictionaryUsingKeys:@"foo", nil]; [[theValue(result.count) should] equal:theValue(0)]; }); }); context(@"matching something", ^{ it(@"returns a subset dictionary", ^{ NSDictionary *result = [subject subdictionaryUsingKeys:@"a", nil]; [[result should] equal:[NSDictionary dictionaryWithObject:@"1" forKey:@"a"]]; }); }); context(@"matching ALL THE THINGS", ^{ it(@"returns a copy of the dictionary", ^{ NSDictionary *result = [subject subdictionaryUsingKeys:@"a", @"b", @"c", nil]; [[result should] equal:[subject copy]]; }); });

Slide 12

Slide 12 text

Frank • Ruby- and Cucumber-based • Gherkin language • “Selenium for native iOS apps” • Encourages accessibility

Slide 13

Slide 13 text

Frank - Feature File Feature: Login to the app Scenario: Successful login Given I launch the app When I log in with a valid userid and password Then I am on the start view

Slide 14

Slide 14 text

Frank - Simple Step Then /^I should see (.*) apples$/ do |count| apples = frankly_map( "label marked:'red apples'", 'tag' ) apples.count.should == count.to_i end

Slide 15

Slide 15 text

Frank - Textfield Step When /^I use the keyboard to fill in the textfield marked "([^\\"]*)" with "([^\\"]*)"$/ do |text_field_mark, text_to_type| text_field_selector = "view marked:'#{text_field_mark}'" check_element_exists( text_field_selector ) touch( text_field_selector ) frankly_map( text_field_selector, 'setText:', text_to_type ) frankly_map( text_field_selector, 'endEditing:', true ) end

Slide 16

Slide 16 text

Frank - Screencapture Step Then /^I save a screenshot with prefix (\w+)$/ do |prefix| filename = prefix + Time.now.to_i.to_s %x[screencapture #{filename}.png] end

Slide 17

Slide 17 text

Frank - Videocapture Around( '@record' ) do | scenario, block | start_recording block.call stop_recording end

Slide 18

Slide 18 text

UIAutomation • Official Apple developer tool • Uses Instruments • Uses Javascript • Purely integration • Can run external scripts

Slide 19

Slide 19 text

UIAutomation function assertElementPresent(el, el_name) { el_name = el_name ? el_name : ''; if (el.isValid()) { return UIALogger.logPass("Element " + el_name + " is present"); } else { return UIALogger.logFail("Element " + el_name + " is not present"); } } var window = UIATarget.localTarget().frontMostApp().mainWindow(); var repository_list = window.tableViews().firstWithName("Repositories"); var latest_repo = repository_list.cells()[0]; window.navigationBar().leftButton().tap(); latest_repo.tap(); var build_list = window.tableViews().firstWithName("Builds"); assertElementPresent(build_list.cells());

Slide 20

Slide 20 text

KIF • “Keep It Functional” • Objective-C • Integration/functional tests • Scenarios & Steps

Slide 21

Slide 21 text

KIF @interface EXTestController : KIFTestController @end @implementation EXTestController - (void)initializeScenarios; { [self addScenario:[KIFTestScenario scenarioToLogIn]]; // Add additional scenarios you want to test here } @end

Slide 22

Slide 22 text

KIF #import "KIFTestScenario+EXAdditions.h" #import "KIFTestStep.h" #import "KIFTestStep+EXAdditions.h" @implementation KIFTestScenario (EXAdditions) + (id)scenarioToLogIn; { KIFTestScenario *scenario = [KIFTestScenario scenarioWithDescription:@"User can successfully login."]; [scenario addStep:[KIFTestStep stepToReset]]; [scenario addStepsFromArray:[KIFTestStep stepsToGoToLoginPage]]; [scenario addStep:[KIFTestStep stepToEnterText:@"[email protected]" intoViewWithAccessibilityLabel:@"Username"]]; [scenario addStep:[KIFTestStep stepToEnterText:@"thisismypassword" intoViewWithAccessibilityLabel:@"Password"]]; [scenario addStep:[KIFTestStep stepToTapViewWithAccessibilityLabel:@"Log In"]]; // Verify that the login succeeded [scenario addStep:[KIFTestStep stepToWaitForTappableViewWithAccessibilityLabel:@"Welcome"]]; return scenario; } @end

Slide 23

Slide 23 text

KIF #import "KIFTestStep+EXAdditions.h" @implementation KIFTestStep (EXAdditions) + (NSArray *)stepsToGoToLoginPage; { NSMutableArray *steps = [NSMutableArray array]; // Dismiss the welcome message [steps addObject:[KIFTestStep stepToTapViewWithAccessibilityLabel:@"That's awesome!"]]; // Tap the "I already have an account" button [steps addObject:[KIFTestStep stepToTapViewWithAccessibilityLabel:@"I already have an account."]]; return steps; } @end

Slide 24

Slide 24 text

KIF - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // ... #if RUN_KIF_TESTS [[EXTestController sharedInstance] startTestingWithCompletionBlock:^{ // Exit after the tests complete so that CI knows we're done exit([[EXTestController sharedInstance] failureCount]); }]; #endif return YES; }

Slide 25

Slide 25 text

that’s it!