-
Notifications
You must be signed in to change notification settings - Fork 369
Getting Started
This guide gives an introduction to writing and running tests with Calabash iOS.
This guide expects that you are using a managed Ruby (e.g. rbenv, rvm) or the Calabash Sandbox.
Calabash iOS requires Ruby >= 2.0 and Xcode >= 6.0.1. The latest release of both are preferred.
From Xcode, start your app with the -cal scheme in iOS simulator.
In Xcode, select the -cal scheme (or similar) and do CMD-R to run. Look at the log output and verify that you see:
DEBUG CalabashServer:242 | Creating the server: <LPHTTPServer: 0xdeadbeef5480>
DEBUG CalabashServer:243 | Calabash iOS server version: CALABASH VERSION: 0.19.1
DEBUG CalabashServer:246 | App Base SDK: iphonesimulator9.3
DEBUG CalabashServer:278 | IPHONE_SIMULATOR_ROOT: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk
DEBUG CalabashServer:264 | Calabash iOS server is listening on: 10.0.0.13 port 37265
If those messages are there, you're good to go.
Test are run using Cucumber and written in a special and particularly readable language: Gherkin.
In your project directory you can run the following command to setup a features directory for your tests (if you didn't already do this in the installation phase).
$ calabash-ios gen
----------Question----------
I'm about to create a subdirectory called features.
features will contain all your calabash tests.
Please hit return to confirm that's what you want.
---------------------------
<RETURN>
----------Info----------
features subdirectory created.
Try executing
cucumber
---------------------------
This generated a features skeleton dir for your tests:
$ tree -L 1 features/
features/
├── my_first.feature
├── step_definitions
└── support
This step also copies the scripts for interactive exploration and test into your working directory (see below).
##Running your tests
Now try running the feature my_first.feature with cucumber.
$ cucumber
Feature: Running a test
As an iOS developer
I want to have a sample feature file
So I can begin testing quickly
Scenario: Example steps
... #different output here
Given I am on the Welcome Screen # features/step_definitions/my_first_steps.rb:1
Then I swipe left # calabash-cucumber-0.9.127.pre1/features/step_definitions/calabash_steps.rb:190
And I wait until I don't see "Please swipe left" # calabash-cucumber-0.9.127.pre1/features/step_definitions/calabash_steps.rb:128
And take picture # calabash-cucumber-0.9.127.pre1/features/step_definitions/calabash_steps.rb:185
1 scenario (1 passed)
4 steps (4 passed)
0m15.097s
If everything went well it should have started the iOS simulator with your app, run the sample test and taken a screenshot (saved as screenshot_0.png). Obviously we'd want to do more than just screenshotting. We'll get to that next!
If something went wrong it is most likely because your XCode is building your app in a non-expected location. The default location is ~/Library/Developer/Xcode/DerivedData/??-xyz/Build/Products/Debug-iphonesimulator/??-cal.app
. Where ?? is your app name and xyz is a random set of characters. If you are building to another location you need to specify the environment variable APP: set it the path where Xcode placed your built .app folder. (Alternatively open the file features/support/01_launch.rb
and comment-in APP
.)
To improve your workflow, we recommend that you use command-line build scripts to stage your .app and .ipa to a known location. For examples see:
- https://github.com/calabash/ios-smoke-test-app
- https://github.com/calabash/ios-webview-test-app
You can target a specific simulator using the DEVICE_TARGET
environment variable.
$ DEVICE_TARGET="iPhone 6 (8.3 Simulator)" cucumber
$ DEVICE_TARGET="8AA57D7F-0805-42A1-ACAE-2E819CC5EAD2" cucumber
To see a list of available simulators, use the instruments
command:
$ xcrun instruments -s devices
For a list of all the environment variables that Calabash iOS responds to see the official documentation.
First a note of warning: Using pre-defined steps to build real test suites is bad practice. The predefined steps are not phrased in the business language of your app, and will be uninteresting to anyone but the tester responsible for automation. This is not what Cucumber is for. We recommend The Cucumber Book to understand the intention and strengths of Cucumber.
In addition, the predefined steps are limited in functionality and are slower than what you can otherwise build.
With all that said, we do provide a number of simple predefined steps that will give you a feeling of how calabash runs. Once you have that feeling and you understand calabash better, you should write your tests using the Ruby API
Take a look at the file features/my_first.feature
:
$ cat features/my_first.feature
Feature: Running a test
As an iOS developer
I want to have a sample feature file
So I can begin testing quickly
Scenario: Example steps
Given I am on the Welcome Screen
Then I swipe left
And I wait until I don't see "Please swipe left"
And take picture
This feature has a single scenario (Example steps) which should correspond to a use case. It probably doesn't make any sense for your application or the sample app. But you can try one of the predefined steps to touch a view on screen. In the sample app, let's try and touch the tab bar button Second
using the predefined step I touch "..."
:
Feature: Running a test
As an iOS developer
I want to have a sample feature file
So I can begin testing quickly
Scenario: Example steps
Given I am on the Welcome Screen
And I touch "Second"
and run
$ cucumber
...
Given I am on the Welcome Screen # features/step_definitions/my_first_steps.rb:1
And I touch "Second" # calabash-cucumber-0.9.74/features/step_definitions/calabash_steps.rb:15
1 scenario (1 passed)
2 steps (2 passed)
0m4.585s
You can continue in this fashion using the predefined steps defined in Calabash. They are documented here:
Soon you'll want to define your own steps. This is documented in detail here:
But before you do that. Please read:
A nice way to use calabash is to explore it interactively. To start-up a console, you can run the command: calabash-ios console
and then start a simulator with start_test_server_in_background
for iOS7 to get touch events to work.
Try starting your app using the -cal scheme from Xcode and then run calabash-ios console
.
This will give you a Calabash console (it is just a Ruby irb with calabash loaded).
From this console you can explore your application interactively.
You can query, touch, scroll, etc from the irb session. You'll see how below. By the way. This information below also serves as an introduction for the APIs that are available when writing custom steps as described in 03-Writing-custom-steps.
Notice that the sample app has two buttons. Now try this from the console:
irb > query("button")
You should see something like this:
=> [{"class"=>"UIRoundedRectButton", "frame"=>{"y"=>287, "width"=>72, "x"=>100, "height"=>37}, "UIType"=>"UIControl", "description"=>"<UIRoundedRectButton: 0x7d463d0; frame = (100 287; 72 37); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x7d46ae0>>"}, {"class"=>"UIRoundedRectButton", "frame"=>{"y"=>215, "width"=>73, "x"=>109, "height"=>37}, "UIType"=>"UIControl", "description"=>"<UIRoundedRectButton: 0x7d3a760; frame = (109 215; 73 37); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x7d3a8a0>>"}]
This is actually an array with two objects that are descriptions of the two buttons. For example, the class of the first button is "UIRoundedRectButton". We can dive into this information using Ruby programming:
irb > result = query("button")
...
irb > btn1 = result[0]
...
irb > class1 = btn1["class"]
=> "UIRoundedRectButton"
The query
function takes a string query as an argument. The query argument is similar to a css selector, for example we can do:
irb > query("button index:0 label")
=> [{"class"=>"UIButtonLabel", "frame"=>{"y"=>9, "width"=>38, "x"=>17, "height"=>19}, "UIType"=>"UIView", "description"=>"<UIButtonLabel: 0x7d41120; frame = (17 9; 38 19); text = 'other'; clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x7d40510>>"}]
This means "find the first button, and then inside of that find all labels".
Query may also take parameters that are mapped to Objective-C selectors on the found object.
irb > query("button index:0 label", :text)
=> ["other"]
Query is very powerful, but the full syntax and power of query is beyond the scope of the Getting started guide. It is documented in the wiki at Query syntax.
Anything that can be found using query can also be touched. Try this while you watch the iOS Simulator:
irb > touch("button index:0")
Notice that the button is touched (turns blue), although this button doesn't do anything.
You can also touch the tab bars:
irb > touch("tabBarButton index:1")
The filter: index:1
means that it is the second tab-bar button that should be touched.
In general UI views are found using accessibility ids or labels. Go to the "First" tab in simulator, and then in your console session try this:
# query for the switch on the "First" tab
irb > query("view marked:'switch'")
[
[0] {
"class" => "UISwitch",
"frame" => {
"y" => 148,
"width" => 79,
"x" => 106,
"height" => 27
},
"UIType" => "UIControl",
"description" => "<UISwitch: 0x7d3ffb0; frame = (106 148; 79 27); <snip> >>"
}
]
You can set the accessibility attributes of UIView from the Interface Builder or programmatically.
In some cases the UIKit framework will set the accessibilityLabel for you if you don't explicitly set the value.
For example, UITabBarButton will have an accessibility label that is the same as title of the tab bar button.
irb > query("tabBarButton marked:'Second'")
[
[0] {
"class" => "UITabBarButton",
"id" => nil,
"rect" => {
"center_x" => 120,
"y" => 520,
"width" => 76,
"x" => 82,
"center_y" => 544,
"height" => 48
},
"frame" => {
"y" => 1,
"width" => 76,
"x" => 82,
"height" => 48
},
"label" => "Second",
"description" => "<UITabBarButton: 0x857e010; frame = (82 1; 76 48); <snip> >"
}
]
Whenever possible, you should use the accessibilityIdentifier of UIView and reserve the accessibilityLabel for providing localized Accessibility traits.
- (void) viewDidLoad {
[super viewDidLoad];
UISwitch *switch = self.wantsCoffeeSwitch;
# ex. queries
# query "view marked:'wants coffee'"
# query "switch {accessibilityIdentifier LIKE 'wants coffee'}"
# query "switch marked:'wants coffee'"
switch.accessibilityIdentifier = @"wants coffee";
# accessibilityLabels should be localized and follow the conventions
# described by Apple's Accessibility documentation
if (switch.isOn == YES) {
switch.accessibilityLabel = NSLocalizedString(@"Wants coffee");
} else {
switch.accessibilityLabel = NSLocalizedString(@"Does not want coffee");
}
}
We suggest that you can jump into writing Custom steps.