Slide 1

Slide 1 text

Victor Ilyukevich @yas375 Contracted iOS developer at Care Zone Inc.

Slide 2

Slide 2 text

Why do I need to spend time writing tests? it reduces bugs it helps to produce good code it proves your code is working as supposed to tests are documentation to your code it makes easy to maintain your code ...

Slide 3

Slide 3 text

UIAutomation

Slide 4

Slide 4 text

UIAutomation

Slide 5

Slide 5 text

UIAutomation var  target  =  UIATarget.localTarget(); var  appWindow  =  target.frontMostApp().mainWindow(); appWindow.tabBar().buttons()["Unit  Conversion"].tap();

Slide 6

Slide 6 text

Tuneup JS http://www.tuneupjs.org

Slide 7

Slide 7 text

Tuneup JS http://www.tuneupjs.org test("Login screen", function(target, app) { var window = app.mainWindow(); // tap the left button in the navigation bar window.navigationBars()[0].leftButton().tap(); // assert that the app has navigated into a sub-view controller assertEquals("Settings", window.navigationBars()[0].value()); });

Slide 8

Slide 8 text

mechanic.js https://github.com/jaykz52/mechanic //  all  UIAStaticText  elements  directly  descended  from  a   tabbar $('tabbar  >  text'); //  all  buttons  inside  the  window  named  'Main' $('window[name=Main]  button') //  the  text  field  with  a  value  of  'Search' $('textfield[value=Search]') //  all  buttons  plus  a  specific  element  by  name $('button,  #Continue')

Slide 9

Slide 9 text

Bwoken iOS UIAutomation Test Runner http://bendyworks.github.io/bwoken/

Slide 10

Slide 10 text

Bwoken adds easy commands to run tests from CLI

Slide 11

Slide 11 text

Bwoken adds easy commands to run tests from CLI $  rake

Slide 12

Slide 12 text

Bwoken adds easy commands to run tests from CLI $  rake $  rake  compile  &&  rake  test equals to:

Slide 13

Slide 13 text

Bwoken adds easy commands to run tests from CLI $  rake $  rake  compile  &&  rake  test $  SIMULATOR=true  rake

Slide 14

Slide 14 text

Bwoken adds easy commands to run tests from CLI $  rake $  rake  compile  &&  rake  test $  SIMULATOR=true  rake $  RUN=iphone/focused_test  rake

Slide 15

Slide 15 text

Bwoken adds easy commands to run tests from CLI

Slide 16

Slide 16 text

Bwoken adds easy commands to run tests from CLI allows you to run tests with CoffeeScript

Slide 17

Slide 17 text

Bwoken adds easy commands to run tests from CLI allows you to run tests with CoffeeScript fill = (container, liquid = "coffee") -> "Filling the #{container} with #{liquid}..."

Slide 18

Slide 18 text

Bwoken adds easy commands to run tests from CLI allows you to run tests with CoffeeScript fill = (container, liquid = "coffee") -> "Filling the #{container} with #{liquid}..." var fill; fill = function(container, liquid) { if (liquid == null) { liquid = "coffee"; } return "Filling the " + container + " with " + liquid + "..."; };

Slide 19

Slide 19 text

Bwoken adds easy commands to run tests from CLI allows you to run tests with CoffeeScript http://coffeescript.org

Slide 20

Slide 20 text

UIAutomation + Tuneup JS + Bwoken + CoffeeScript

Slide 21

Slide 21 text

UIAutomation + Tuneup JS + Bwoken + CoffeeScript #import  "../helpers/Caremobile.js"   Caremobile.resetAppState() SignInScreen.signIn('testuser',  'testpass')   test  'Caregiver  adds  a  beloved',  (target,  app)  -­‐>    name  =  'Kurt  Cobain'    BelovedsScreen.tapAddButton()    ProfileScreen.setFields({  'full  name':  name  })    ProfileScreen.tapSaveButton()    BelovedsScreen.assertBeloved({  regex:  new  RegExp(name,  "g")  })   test  'Caregiver  edits  a  beloved',  (target,  app)  -­‐>    ...

Slide 22

Slide 22 text

UIAutomation + Tuneup JS + Bwoken + CoffeeScript class  BelovedsScreenClass  extends  BaseListScreen      assertBeloved:  (args)  -­‐>        @assertCell  args      belovedsList:  -­‐>        Caremobile.window().tableViews()['List  of  loved  ones']      tapBelovedNamed:  (name)  -­‐>        @belovedsList().cells()[name].tap()     BelovedsScreen  =  new  BelovedsScreenClass

Slide 23

Slide 23 text

UIAutomation + Tuneup JS + Bwoken + CoffeeScript $  RUN=beloveds  rake

Slide 24

Slide 24 text

UIAutomation + Tuneup JS + Bwoken + CoffeeScript $  RUN=beloveds  rake

Slide 25

Slide 25 text

UIAutomation + Tuneup JS + Bwoken + CoffeeScript Passing custom params to the app. https://github.com/carezone/bwoken/commit/195f2f9

Slide 26

Slide 26 text

UIAutomation + Tuneup JS + Bwoken + CoffeeScript Passing custom params to the app. desc  "Run  tests" task  :run_tests  do    ENV['INSTRUMENTS']  =  "-­‐e  TEST_MODE  1"    Rake::Task[:default].invoke end Add a new task to your Rakefile: https://github.com/carezone/bwoken/commit/195f2f9

Slide 27

Slide 27 text

UIAutomation + Tuneup JS + Bwoken + CoffeeScript Passing custom params to the app. BOOL  IsTestMode()  {    return  (getenv("TEST_MODE")  !=  nil); } https://github.com/carezone/bwoken/commit/195f2f9

Slide 28

Slide 28 text

Unit testing

Slide 29

Slide 29 text

SetTestingKit.framework

Slide 30

Slide 30 text

SetTestingKit.framework @implementation  MyAppTests   -­‐  (void)setUp  {        [super  setUp];        //  Set-­‐up  code  here. }   -­‐  (void)tearDown  {        //  Tear-­‐down  code  here.        [super  tearDown]; }   -­‐  (void)testExample  {        STFail(@"Unit  tests  are  not  implemented  yet  in  MyAppTests"); }   @end

Slide 31

Slide 31 text

SetTestingKit.framework

Slide 32

Slide 32 text

SetTestingKit.framework

Slide 33

Slide 33 text

SetTestingKit.framework https://github.com/mysterioustrousers/MTDates

Slide 34

Slide 34 text

SetTestingKit.framework

Slide 35

Slide 35 text

SetTestingKit.framework

Slide 36

Slide 36 text

Kiwi https://github.com/allending/Kiwi

Slide 37

Slide 37 text

Why do I like Kiwi. expectations

Slide 38

Slide 38 text

Why do I like Kiwi. expectations [[subject  should]  beTrue]; [[subject  should]  equal:(id)anObject]; [[subject  should]  beLessThan:(id)aValue]; [[@"Hello,  world!"  should]  containString:@"world"]; [[subject  should]  beKindOfClass:(Class)aClass]; [[subject  should]  haveCountOf:(NSUInteger)aCount];   NSArray  *array  =  [NSArray  arrayWithObject:@"foo"]; [[array  should]  have:1]  item];   [[subject  should]  receive:(SEL)aSelector  andReturn:(id)aValue]; [[theBlock(^{  ...  })  should]  raise];

Slide 39

Slide 39 text

Why do I like Kiwi. expectations ‘describe’, ‘context’, ‘before each’, ‘before all, ‘after each’, ‘after all’ blocks

Slide 40

Slide 40 text

Why do I like Kiwi. expectations ‘describe’, ‘context’, ‘before each’, ‘before all, ‘after each’, ‘after all’ blocks mocks objects that imitate a class, or look like they conform a protocol

Slide 41

Slide 41 text

Why do I like Kiwi. expectations ‘describe’, ‘context’, ‘before each’, ‘before all, ‘after each’, ‘after all’ blocks mocks objects that imitate a class, or look like they conform a protocol stubs return canned responses on method calls

Slide 42

Slide 42 text

Why do I like Kiwi. id  mock  =  [Animal  mock]; [[mock  should]  beMemberOfClass:[Animal  class]];   [mock  stub:@selector(species)  andReturn:@"P.  tigris"]; [[mock.species  should]  equal:@"P.  tigris"]; [Cruiser  stub:@selector(classification)  andReturn:@"Not  a  moon"]; [[[Cruiser  classification]  should]  equal:@"Not  a  moon"];

Slide 43

Slide 43 text

Got it. Show me real code!

Slide 44

Slide 44 text

Got it. Show me real code! #import   #import  "Matrix.h" SPEC_BEGIN(MatrixSpec)    describe(@"Matrix",  ^{        __block  Matrix  *matrix  =  nil;        context(@"initialized  with  size  of  3",  ^{            beforeEach(^{                matrix  =  [[Matrix  alloc]  initWithSize:3];            });            it(@"has  size  of  3",  ^{                [[theValue(matrix.size)  should]  equal:theValue(3)];            });            context(@"sets  value  3  to  row  2  column  1",  ^{                beforeEach(^{                    [matrix  setValue:@3  forRow:2  column:1];                }); ...            });        });    }); SPEC_END

Slide 45

Slide 45 text

Got it. Show me real code! describe(@"CalendarEvent",  ^{    __block  CalendarEvent  *event  =  nil;    beforeEach(^{        NSManagedObjectContext  *context  =  [NSManagedObjectContext                                                          makeMainQueueContext];        event  =  [CalendarEvent  insertIntoContext:context];    });        context(@"when  current  time  is  23:44",  ^{        it(@"starts  at  00:00",  ^{            [[NSDate  stubAndReturn:[NSDate  dateFromYear:2012                  month:1  day:23  hour:23  minute:44]]  date];            [event  setupDefaultDates];            [[event.startsAt  should]  equal:[NSDate  dateFromYear:2012                                  month:1  day:24  hour:0  minute:0]];        });    }); });

Slide 46

Slide 46 text

Alternatives

Slide 47

Slide 47 text

Cedar a bunch of matchers stubs and mocks describe and context blocks before each and after each blocks pending specs focused specs reporters https://github.com/pivotal/cedar

Slide 48

Slide 48 text

-­‐  (void)testAuthorizationHeaderWithInvalidUsernamePassword  {        [Expecta  setAsynchronousTestTimeout:5.0];                __block  NSHTTPURLResponse  *response  =  nil;        [self.client  getPath:@"/basic-­‐auth/username/password"                            parameters:nil                                  success:NULL                                  failure:^(AFHTTPRequestOperation  *operation,  NSError   *error)  {                response  =  operation.response;        }];                expect(response.statusCode).will.equal(401); } OCMock + Expecta

Slide 49

Slide 49 text

-­‐  (void)testAuthorizationHeaderWithInvalidUsernamePassword  {        [Expecta  setAsynchronousTestTimeout:5.0];                __block  NSHTTPURLResponse  *response  =  nil;        [self.client  getPath:@"/basic-­‐auth/username/password"                            parameters:nil                                  success:NULL                                  failure:^(AFHTTPRequestOperation  *operation,  NSError   *error)  {                response  =  operation.response;        }];                expect(response.statusCode).will.equal(401); } OCMock + Expecta https://github.com/AFNetworking/AFNetworking

Slide 50

Slide 50 text

Other alternatives OCMochito OCHamcrest GHUnit ... http://nshipster.com/unit-testing/

Slide 51

Slide 51 text

Build and run from CLI

Slide 52

Slide 52 text

Build and run from CLI xcodebuild

Slide 53

Slide 53 text

xcodebuild $  xcodebuild  -­‐workspace  MyApp.xcworkspace  \                          -­‐scheme  MyScheme  \                          -­‐sdk  iphonesimulator  \                          -­‐configuration  Release  \                          build

Slide 54

Slide 54 text

xcodebuild desc  "Run  unit  tests" task  :unittest  do    kill_simulator      cmd  =  "xcodebuild  -­‐workspace  #{WORKSPACE_NAME}.xcworkspace  \                                        -­‐scheme  #{SCHEME_NAME}  \                                        -­‐sdk  iphonesimulator  \                                        -­‐configuration  Release                                          RUN_UNIT_TEST_WITH_IOS_SIM=YES  \                                        build"    stdout  =  TestOutput.new(STDOUT)    stderr  =  TestOutput.new(STDERR)    status  =  Open4.spawn(cmd,                                              :stdout  =>  stdout,                                              :stderr  =>  stderr,                                              :status  =>  true)    exit  status.exitstatus end

Slide 55

Slide 55 text

xcodebuild

Slide 56

Slide 56 text

Build and run from CLI xcodebuild

Slide 57

Slide 57 text

Build and run from CLI xcodebuild xctool

Slide 58

Slide 58 text

xctool $  brew  install  xctool

Slide 59

Slide 59 text

xctool $  brew  install  xctool $  xctool  -­‐workspace  App.xcworkspace\                  -­‐scheme  App\                  test

Slide 60

Slide 60 text

xctool .xctool-args [    "-­‐workspace",  "App.xcworkspace",    "-­‐scheme",  "App" ]

Slide 61

Slide 61 text

xctool .xctool-args [    "-­‐workspace",  "App.xcworkspace",    "-­‐scheme",  "App" ] $  xctool  test

Slide 62

Slide 62 text

xctool

Slide 63

Slide 63 text

Continuous integration (CI)

Slide 64

Slide 64 text

Continuous integration (CI) Jenkins Travis CI http://jenkins-ci.org http://travis-ci.org

Slide 65

Slide 65 text

Travis CI Provides hosted CI service free for open source (public repo) paid for private repositories A hosted continuous integration service for the open source community

Slide 66

Slide 66 text

Travis CI A hosted continuous integration service for the open source community language:  objective-­‐c before_install:    -­‐  brew  update    -­‐  brew  install  xctool script:  xctool  test Setup for open source project Add .travis.yml file at root of your repository

Slide 67

Slide 67 text

Travis CI A hosted continuous integration service for the open source community Login at http://travis-ci.org and enable it Add .travis.yml file at root of your repository Setup for open source project

Slide 68

Slide 68 text

Travis CI A hosted continuous integration service for the open source community

Slide 69

Slide 69 text

Travis CI A hosted continuous integration service for the open source community Login at http://travis-ci.org and enable it Add .travis.yml file at root of your repository Setup for open source project Add nice status picture to your README

Slide 70

Slide 70 text

Travis CI A hosted continuous integration service for the open source community

Slide 71

Slide 71 text

Travis CI A hosted continuous integration service for the open source community

Slide 72

Slide 72 text

Travis CI A hosted continuous integration service for the open source community

Slide 73

Slide 73 text

Travis CI A hosted continuous integration service for the open source community

Slide 74

Slide 74 text

When to start writing tests?

Slide 75

Slide 75 text

When to start writing tests? NOW

Slide 76

Slide 76 text

Thanks! Victor Ilyukevich @yas375