How would you design one Appium gesture layer when UiAutomator2 and XCUITest share no command names?
answer
- decide where the divergence lives
- one adapter owns the mapping
- no counterpart means fail loudly
- logs must name the posted command
basics
~20 sPut the per-driver mapping in one adapter behind a small facade, keep only genuinely corresponding gestures in the portable vocabulary, and make an unsupported gesture fail loudly rather than be approximated. Log the command actually posted.
solid answer
~40 sThe decision is not whether to abstract but **where the divergence is allowed to live**. Put it in one adapter: a narrow facade offers the gestures that genuinely correspond on both drivers — swipe by direction, tap, long press — and one place translates each into `mobile: swipeGesture` with `percent` on UiAutomator2 or `mobile: swipe` on XCUITest. Everything without a counterpart, such as `mobile: flingGesture` on one side or `mobile: forcePress` on the other, stays outside the facade as an explicitly driver-scoped call that throws where it is unsupported. Two rules make the layer safe: never approximate a missing gesture with a similar one, because a green run then proves nothing; and always log the command actually posted, so triage does not have to reverse-engineer the branch.
code
java · 9 linesvoid swipeUpOnHutList(String elementId) {
if (isUiAutomator2Session) {
driver.executeScript("mobile: swipeGesture",
Map.of("elementId", elementId, "direction", "up", "percent", 0.8));
} else {
driver.executeScript("mobile: swipe",
Map.of("elementId", elementId, "direction", "up"));
}
}go deeper
You are not expected to design this layer, but recognise the shape: one place in the suite decides between mobile: swipeGesture and mobile: swipe, and your test code calls that place rather than the driver.
Be ready to describe the adapter concretely: which gestures it covers, what it branches on, and why an argument such as percent cannot simply be forwarded to the other driver.
Argue the failure modes. Show why approximating an unsupported gesture is worse than a red pipeline, and how logging the posted command keeps triage cheap once the facade hides the branch.
Own the policy: how wide the portable vocabulary is, what happens at the edges, and how new gestures enter it. Be able to defend the seam you kept rather than the abstraction you avoided.
## The decision, stated honestly The drivers share no gesture command names, so somewhere in your suite a branch has to exist. The only real question is where it lives, how wide it is, and what it does at the edges. Hiding it entirely is a false goal: `percent` is mandatory on Appium's UiAutomator2 swipe and has no XCUITest counterpart at all, so a facade cannot make the two calls equivalent — it can only choose which of them the test author sees. That is why this is a lead's call rather than a refactor. It is a decision about which lies the abstraction is allowed to tell. ## Three shapes, and what each costs | Shape | What test code sees | What it costs | |---|---|---| | One facade, per-driver adapter | `swipeUp(hutList)` | The facade must silently pick a `percent` on one driver that has no meaning on the other | | No facade, driver commands inline | `mobile: swipeGesture` / `mobile: swipe` | Honest and greppable, but every gesture is written twice and drifts | | Narrow portable core plus explicit driver-scoped calls | `swipeUp(hutList)` plus `forcePress(...)` that throws off-driver | More design work up front; the divergence is visible exactly where it is real | The third shape is usually the right answer for a suite that covers both platforms seriously, and the first is the one teams reach for and regret. ## Where the seams go A workable layer is decided by four choices, and they are worth making explicitly rather than letting the first helper set them: - **Which gestures are portable.** Only the ones with a genuine counterpart on both drivers: a directional swipe, a tap, a long press, a pinch that opens or closes. Keep the list short and written down. - **What the portable vocabulary may take as arguments.** If the facade accepts a fraction of a list, it can only honour it on one driver. Prefer arguments both drivers can express, and let the driver-specific tuning live in the adapter. - **What the branch key is.** The driver the session started with, never `platformName`, because Appium's Espresso driver is an Android driver that names its swipe like XCUITest's. - **What happens off-driver.** A gesture with no counterpart throws a clear unsupported error naming the driver, and the test that needs it is marked as platform-specific rather than pretending to be portable. ## The rule for a gesture with no twin This is the rule that most often gets broken under deadline pressure, and it deserves stating as policy: 1. `mobile: flingGesture` is UiAutomator2's and XCUITest has no fling. Do not emulate it with a fast `mobile: swipe` and call the coverage equal. 2. `mobile: forcePress` and `mobile: rotateDigitalCrown` are XCUITest's. Do not emulate a force press with `mobile: longClickGesture`; a pressure input and a duration input are different things and the app distinguishes them. 3. Where the shapes genuinely differ — two pinch commands on one driver, one on the other — the adapter converts, and the conversion is documented next to the code rather than in someone's memory. Approximation is attractive because it turns a red pipeline green. It is exactly the failure mode a lead is hired to prevent: the suite still runs, still reports success, and no longer tests the input the user actually makes. ## Keeping the layer debuggable A gesture facade is a place where failures get harder to read, and that is a design cost you have to pay down deliberately: - Log the `mobile:` name and the parameter map actually posted, so a failure names the real command rather than the facade method. - Keep the adapter thin enough that a reader can see the whole per-driver mapping on one screen. - Assert the effect of every gesture on the mountain-hut screen, so a mis-mapped command shows up as a failed assertion instead of a passing step. - Review new gestures as a pair: adding one to the portable vocabulary is a change on both drivers or on neither. ## What a lead is actually being asked The question rewards someone who names the tradeoff instead of picking a pattern. The strong answer says: the divergence is real, it belongs in one adapter, the portable vocabulary is deliberately small, anything without a counterpart is explicitly platform-scoped and fails loudly, and the logs never lose which driver command ran. The weak answer promises a facade that makes the platforms look identical, which is the promise the argument maps cannot keep.
- Where would you draw the line between a portable gesture and a platform-only one?A gesture is portable only when both drivers declare a command with the same effect and the facade can honour every argument it exposes. A directional swipe, tap and long press qualify. A fling or a force press does not, because one driver has no command for it, so those stay driver-scoped and their tests are marked platform-specific.
- How do you keep a gesture facade from making a failing run look green?Never let it substitute a near-equivalent command; an unsupported gesture throws. Assert the on-screen effect after every gesture rather than the command's return, and log the mobile: name and parameter map actually posted so a mis-mapped call is visible in the failure rather than hidden behind the facade method name.
It is the difference between a travel adapter and rewiring the appliance: you accept one deliberate seam at the boundary, rather than pretending every socket in the world is the same shape.
saying these in an interview costs you the question
- Assumes one facade can hide every gesture difference cleanly.
- Approximates a missing gesture with the nearest similar one.
- Hides which driver command actually ran from the logs.
- Copies the per-driver mapping into every page object.
- Exposes a percent argument the Apple driver cannot honour.
- Treats a green pipeline as proof the intended input was sent.