At Badoo, we run more than 1200 end-to-end tests for our main iOS application.
This should amount to more than 40 hours of tests, but we run them in just 1.5 hours. And now, thanks to some updates, we've got it down to a mere 35 minutes.
In this talk, I will describe how we moved from tests tightly coupled to infrastructure (simulator hosts and tools) to an iOS remote device server. I'll also explain how this simplified our parallel test runs and made our tests and infrastructure easier to maintain and scale.
2. Hello, my name is
Nikolai Abalov
Senior Automation Engineer at Badoo
Previously Senior Developer and Automation Engineer
at 2GIS for 3 years
Co-creator of Winium.Mobile
From Siberia
github.com/NickAb
@NickAb
3. Agenda
1. Introduction
2. Parallel tests on iOS
3. Device Server
4. Results & next steps
5. Know-Hows — how to apply it at your scale
6. Recap
3
5. What we had
● 10 Pull Requests per day to the app repo
● 1200+ end-to-end tests
● 35+ hours of cpu-time
● 1.5 hours wall-clock time
● 1500+ cpu-time hours per day
● Extra projects on the same infrastructure
● Tightly coupled tests and infrastructure
5
6. What we want
● People trusting the automation
● Fast and reliable feedback
6
7. What we want
● 30 minutes wall-clock
● Stability
● Scalability
● Easy maintenance and development
7
10. Joking aside
● Filter tests based on change set, coverage, components
● Speed up and stabilise tests
● Heavily parallelise test runs
● Provide scalable infrastructure to run tests
10
14. Multiple users, simulator per user
Agent
Rake / Parallel Cucumber
ssh qa1@h1 ssh qa2@h1 ssh qa1@h2 ssh qa2@h2
macOS,
Xcode,
Ruby,
…
14
Test Test Test Test
iOS Calabash in parallel on one host by Tim Baverstock
15. Single user, multiple simulators
Agent
Rake / Parallel Cucumber
ssh qa@h1 ssh qa@h1 ssh qa@h2 ssh qa@h2
15
Test Test Test Test Test Test
iOS Parallel Automation: run faster than fast by Viktar Karanevich
Turning iOS End to End Testing up to 11 by Lawrence Lomax
macOS,
Xcode,
Ruby,
…
16. Issues with current model
● Tight coupling of test code and infrastructure
● Hard to scale
● No sharing of resources between test jobs on CI
● Tests are bound to simulator host machine
● Too easy to mess with simulator internals
16
18. New model
Not bound
to macOS
18
Device Server
Agent
Rake / Parallel Cucumber
http
Test Test Test Test Test Test
http http http http http
19. New model
Device Server
ssh ssh ssh ssh
macOS,
Xcode, …
19
Agent
Rake / Parallel Cucumber
Not bound
to macOS
http
Test Test Test Test Test Test
http http http http http
ssh ssh
20. New model
● Separation of concerns
● Well-defined interface
● Cheaper test environments
● Easy sharing of resources between test jobs on CI
20
21. Why move? Sharing
t Agent 1 Simulators Agent 2 Simulators
1
2
3
4
5
6
t Shared Simulators
1
2
3
4
21
before after
22. More reasons to move
● Black boxing
● Easier updates
● No more xcrun simctl, fbsimctl, idevice*
● No more messing with internals
22
23. ● Did not fit well with existing code
● We need custom actions
● We need flexibility and fast prototyping
Why not Selenium Grid?
23
31. ● Prepare and reset simulators on demand
● Start WebDriverAgent on simulators
● Hide away satisfying desired requirements
● Extra actions
● Pre-boot/Pool simulators
Why we need something on top of fbsimctl?
31
32. Remote Device API (Subset)
class RemoteDevice
...
def reset(timeout: 60); end
def clear_safari_cookies; end
...
def app_installed?(bundle_id); end
def create_driver(caps); end
...
def video_acquire; end
def video_start; end
def video_stop; end
def last_crash_log; end
32
50. Remote Device API
class RemoteDevice
attr_reader :info
attr_reader :wda_endpoint
attr_reader :calabash_port
attr_reader :device_ref
def initialize(server_endpoint, device_ref); end
def release; end
def reset(timeout: 60); end
def await(timeout: 60); end
def set_location(lat:, lon:); end
def clear_safari_cookies; end
50
51. Remote Device API
def approve_access(bundle_ids); end
def app_installed?(bundle_id); end
def install_app(app_bundle_path); end
def launch_app(bundle_id); end
def list_apps; end
def open_url(url); end
def relaunch_app(bundle_id); end
def terminate_app(bundle_id); end
def uninstall_app(bundle_id); end
51
52. Remote Device API
def create_driver(caps); end
def user_ports; end
def endpoint_for(port); end
def screenshot(filetype, prefer_wda: true); end
def video_acquire; end
def video_start; end
def video_stop; end
def last_crash_log; end
end
52