UI tests without UI Testing experiment
This repo is a small experiment to see if there's an "in-between" for testing iOS applications. More feature-level than XCTest but not as heavy handed (or slow and brittle) as UI Testing.
The general idea is to provide an API similar to UI Testing but be able to spin up any controller on the fly. By putting the controller inside of a window the test behaves a bit more like the real app.
This extends on my thoughts in a recent blog, Testing the UI without UI Testing in Swift.
Is this crazy?
Probably! But maybe this is something worth exploring.
Have you tried anything like this? Is this obviously a maintenance nightmare? I would love to hear what you think.
continueAfterFailure = false // Code-powered views let controller = SomeViewController() let app = App(controller: controller) // Storyboard-powered views let controller = SomeViewController() let app = App(storyboard: "Main", identifier: "Some identifier") // ...
See the unit tests for more examples.
First, create a reference to an
app instance with your controller, as shown above.
The "top" view controller's view will be searched. For example, a controller pushed onto a navigation stack or presented modally.
The finders ignore views that are disabled or not in the controller's view's frame. For example, a view rendered off the screen cannot be found.
Interaction with elements is ignored if the element is disabled.
let label = try app.label() - find a label via
text or accessibility label/identifier
let button = try app.button() - find a button via
title or accessibility label/identifier
button?.tap() - triggers the target-action for the button
let aSwitch = try app.switch() - find a switch via accessibility label/identifier
aSwitch?.toggle() - triggers the value changed action on the switch
let cell = try app.cell(containingText:) - find the first
UITableViewCell (or subclass) containing a label matching the text
cell?.tap() - taps the cell via its index path and delegate
let stepper = try app.stepper() - find a stepper via accessibility label/identifier
stepper?.increment() - increments the stepper by the step value and triggers the value changed action
stepper?.decrement() - decrements the stepper by the step value and triggers the value changed action
let slider = try app.slider() - find a slider via accessibility label/identifier
slider?.set(value:) - sets the slider to the value and triggers the value changed action
let textField = try app.textField() - find a text field via
placeholder or accessibility label/identifier
textField?.type(text:) - sets the text field's value and calls
textField(_:, shouldChangeCharactersIn:, replacementString:) on the delegate
app.alertViewController?.tapButton(title:) - triggers the attached action and dismisses the alert
- Gesture - swiping and scrolling
- Collection view
- Map view
Out of scope (for now)
- System alert - this probably isn't be possible
- SwiftUI - ViewInspector is probably a better choice