skip to content

In an Appium suite spanning Android and iOS, how do you design one install-and-remove step?

level: principalimportance: should knowfreq 40%

answer

  1. one verb, two adapters
  2. shared names, divergent arguments
  3. artifact, identity, target kind
  4. descriptor per target, not per test
  5. errors must name the platform

basics

~20 s

Keep one verb in the suite and one adapter per platform. The command names match on Android and Apple platforms, so what an adapter varies is the artifact, the app identifier, and whether split APKs need UiAutomator2's multi-apk install.

solid answer

~40 s

Expose a single intent to the suite — make sure the hive-log build is on this target — and put the divergence behind it. `mobile: installApp`, `mobile: removeApp` and `mobile: isAppInstalled` carry the same names on both platforms, so the shared surface is real and worth keeping. What must be parameterised is the **artifact** (an `.apk`, an `.apks`, or loose splits needing UiAutomator2's `mobile: installMultipleApks`, against a single `.ipa` or `.app`), the **identifier** you pass to remove and check, and the **target kind** on Apple platforms, where a simulator build and a device build are not the same file. Resolve all three from a per-target descriptor rather than from a platform branch inside a test, and make sure failures still name the platform they came from.

go deeper

for a junior

Know that the same install and remove command names work on both platforms, and that the file you pass and the identifier you pass are what change between an Android and an Apple lane.

for a middle

Be able to name the three things an adapter must vary — artifact, identifier, target kind — and to explain why the Android side is the only one that ever branches on split builds.

for a senior

Show how you make the step idempotent across repeated runs on shared devices, and how failures stay attributable to one platform and one artifact.

for a principal

Own where the abstraction stops: what this step must not absorb from device-matrix, artifact-selection and signing decisions, and what the suite should record so a cross-platform install failure needs no re-run to diagnose.

## Start from what genuinely generalises A cross-platform abstraction is only honest if something is actually shared. Here something is: `mobile: installApp`, `mobile: removeApp` and `mobile: isAppInstalled` are declared on both sides — by `appium-android-driver` for the UiAutomator2 and Espresso drivers, and by the XCUITest driver in its own execute-method map. The **verbs** are common, and so is the shape of the work: check, optionally remove, install, proceed. So the suite should see one intent, not three commands. Something like *ensure the hive-log build is present on this target* is the right altitude: it is what every test actually needs, it is stable across platforms, and it hides nothing a test author has to reason about. ## What does not generalise — three axes, and only three 1. **The artifact.** Android takes an `.apk`, an `.apks` archive carrying a split set, or loose splits that need UiAutomator2's `mobile: installMultipleApks`. Apple platforms take one `.ipa` archive or one `.app` bundle, and have no multi-artifact command at all. 2. **The identity.** Remove and check are given the app's own identifier, which is a package name on Android and a bundle id on Apple platforms. One field in your descriptor, two spellings. 3. **The target kind on Apple platforms.** A simulator build and a real-device build are different files. A lane that treats platform as the only axis will hand a simulator artifact to hardware sooner or later. Everything else about the step is common. Resist the urge to add a fourth axis for a difference you have not measured. ## A shape that holds - Give every target a **descriptor** — platform, target kind, artifact path or paths, app identifier — resolved once at suite start rather than discovered per test. - Put one adapter behind the shared verb per platform. The Android adapter is the only one that ever branches on artifact shape; the Apple adapter is the only one that branches on simulator versus device. - Make the step **idempotent**: check with `mobile: isAppInstalled`, remove with `mobile: removeApp` when a clean target is required, then install. It must behave the same on a fresh device and on the third run of the day. - Decide the reinstall policy once, centrally, and apply it uniformly. Tests should never each decide whether to reinstall. - Let the adapter, not the test, choose between `mobile: installApp` and `mobile: installMultipleApks`. - Record what happened — target, artifact path, command used — in one log line per session. ## Where the abstraction must stop The strongest failure mode is not a missing branch; it is an abstraction that swallows the divergence and reports a generic error. Three rules keep it honest: - **Errors name the platform and the artifact.** A failure that says only that install failed is worthless across two lanes; one that says which target, which file and which command is a two-minute fix. - **Do not pretend Apple platforms have a split install.** A no-op branch that silently accepts a list of paths on the Apple side is worse than a hard error, because it turns a configuration mistake into a launch crash later. - **Do not absorb neighbouring decisions.** Which devices and platform versions the suite covers, and which build each lane is pointed at, are test-design questions decided elsewhere; how an artifact is signed is its own subject; the capabilities that name an artifact at session start are a different surface again. This step's job is narrow — put the file that was chosen for this target onto that target, and be able to take it off. | decision | shared | per platform | |---|---|---| | command names | yes — install, remove, is-installed | no | | artifact shape | no | `.apk` / `.apks` / splits versus `.ipa` / `.app` | | identifier | no | package name versus bundle id | | target kind | no | simulator and device differ on Apple platforms | | idempotence policy | yes | no | ## How you know it has gone wrong Watch for three symptoms. First, tests that carry platform conditionals — the branch escaped the adapter. Second, an Android lane that passes install and fails at launch — the split set is incomplete and the adapter is choosing the wrong command. Third, a failure message that could have come from either platform — the abstraction is hiding the one fact triage needs. For a beekeeping hive-log suite the payoff is concrete: one setup verb, two small adapters, and a failure that tells you within a line whether the problem is the build you were handed or the target you were handed it for. That is the test of the design — not how little platform code it contains, but how fast it names the platform when something breaks.

  • Where does the Apple simulator-versus-device split belong in that design?
    In the target descriptor, not in the test. The Apple adapter reads the target kind and picks the artifact built for it, because a Simulator `.app` and a device build are different files. Modelling target kind as a first-class field also stops a lane silently reusing yesterday's simulator artifact when it moves to hardware.
  • How do you keep the shared install step from hiding which platform failed?
    Make the adapter attach context to every failure it raises: platform, target identifier, artifact path and the execute method it called. The shared verb is for the caller's convenience, not for the error path — a message that reads the same on both lanes forces a re-run just to learn which one broke.

saying these in an interview costs you the question

  • Puts platform conditionals inside the tests themselves
  • Assumes one artifact serves simulator and real device
  • Adds a no-op split-install branch on the Apple side
  • Lets each test decide whether to reinstall
  • Reports install failures without naming the platform