How would you structure one Appium mobile-web suite when Chrome and Safari sessions need different capabilities?
answer
- share the ask, split the setup
- only standard keys travel unchanged
- the Safari preferences have no Android twin
- assert outcomes, never identical configuration
basics
~20 sKeep a thin shared block, then one explicit block per platform: only browserName and the standard W3C keys are truly shared, while the driver, the browser and every Safari preference diverge. Assert identical outcomes, never identical setup.
solid answer
~40 sStart from what is actually common: `browserName` as the ask, `platformName`, the other standard W3C keys, and every WebDriver command sent after the session exists. Everything else diverges — `appium:automationName` is UiAutomator2 on **Android** and XCUITest on **iOS**, the browser is Chrome versus Safari, the bridge is a Chromedriver process versus Apple's remote web inspector, and `appium:safariInitialUrl`, `appium:safariAllowPopups` and `appium:nativeWebTap` exist only on the Apple side. So the honest shape is a **thin shared block plus one explicit block per platform**, not a clever wrapper that pretends to be portable. Assert the same user-visible outcomes on both; do not chase the same configuration. And name no version numbers you cannot keep true.
code
json · 18 lines{
"shared": {
"acceptInsecureCerts": false,
"appium:newCommandTimeout": 120
},
"android": {
"platformName": "Android",
"appium:automationName": "UiAutomator2",
"browserName": "Chrome"
},
"ios": {
"platformName": "iOS",
"appium:automationName": "XCUITest",
"browserName": "Safari",
"appium:safariInitialUrl": "https://farm.example/weigh-in",
"appium:nativeWebTap": true
}
}go deeper
Know that the Android and iOS browser sessions need different capability blocks, and that browserName plus the standard W3C keys are the parts that look the same on both.
Be able to list what actually diverges: the driver name, the browser, the web bridge, the starting context, and the Safari-only start-up preferences that have no Android counterpart.
Design the merge so the divergence stays visible: a thin shared block, explicit per-platform blocks, no wrapper inventing keys that exist on one side only.
Own the parity claim itself. Decide what the suite asserts on both platforms, what it deliberately configures differently, and how the team reads a one-platform failure without blaming the configuration.
## Start from what is genuinely common The temptation with a mobile-web suite is to write "one config, two platforms" and hide the difference. The useful discipline is the opposite: enumerate what is truly shared first, and treat everything else as explicitly per-platform. Genuinely shared: - **The ask.** `browserName` is how both platforms are told this is a browser session. It is one of the standard W3C capability names, so it needs no vendor prefix on either side. - **The other standard W3C keys**, such as `acceptInsecureCerts` and `platformName` itself. - **A handful of Appium base capabilities** that both drivers accept, written with the `appium:` prefix, such as `appium:newCommandTimeout`. - **Every command after session creation.** Once the session exists you are sending plain WebDriver at a page. That last one is the whole payoff. The steps that log a weigh-in on the farm dashboard — type the weight, pick the animal tag, submit, assert the record appears — are one implementation for both platforms. ## The list that cannot be shared | Concern | Android | iOS | |---|---|---| | Driver | `appium:automationName` UiAutomator2 | `appium:automationName` XCUITest | | Browser | Chrome, package `com.android.chrome` | Safari | | Web bridge | a Chromedriver process | Apple's remote web inspector | | Starting context | `CHROMIUM` | not a `CHROMIUM` context | | Start-up preferences | none in the `appium:safari*` family | `appium:safariInitialUrl`, `appium:safariAllowPopups` | | Tap fidelity knob | none exposed | `appium:nativeWebTap` and its companion settings | | Inspecting what the bridge got | `mobile: getChromeCapabilities` | `mobile: updateSafariPreferences` for preferences at runtime | Read that table as a design input rather than trivia. Almost every row is a place where a "portable" abstraction would have to invent something that does not exist on one side. ## Shape: a thin shared block and two platform blocks The structure that survives contact with a real suite is boring: 1. A shared block holding only keys both drivers genuinely accept. 2. One Android block: the platform, the Android driver, the browser ask. 3. One iOS block: the platform, XCUITest, the browser ask, plus the Safari preferences you actually want. 4. A merge that is a plain overlay, with the platform block last, and no silent dropping of keys it does not recognise. What to refuse: - A wrapper that accepts a made-up key like "initialUrl" and quietly maps it to the iOS capability while doing nothing on Android. It hides a real behavioural difference behind a name that suggests parity. - A shared block that carries `appium:safari*` keys "because Android ignores them anyway". Ignoring is an assumption about someone else's driver; keeping the key where it belongs costs nothing. - A version number pinned in shared config. Pin the *ask*, not a number you will have to chase. ## Assert outcomes, not setup The principal-level move is to relocate the parity claim. Two sessions that were configured identically prove nothing; two sessions that produced the same user-visible result prove something. - **Parity to assert:** the weigh-in is recorded, the total updates, the validation error appears for a negative weight. - **Parity not to chase:** the same capability keys, the same start-up route to the page, the same input mechanism for a tap. That framing also tells the team what a failure means. If the Android run fails and the iOS run passes on the same assertion, that is a real product or engine difference worth investigating. If they differ only in setup, the suite was never claiming parity there in the first place. ## The entry-path trap The sharpest instance is `appium:safariInitialUrl`. Because Safari can be told to open the weigh-in page as part of session creation, the iOS run can start already on the page, while the Android run has to get there itself. Two honest options: - Let iOS use the shortcut and have the Android job perform the navigation as an explicit first step, documenting that the entry paths differ by one action. - Have both runs navigate explicitly, so the entry path is identical and the capability is used only where it earns something, such as pinning a known start page for isolation. Either is defensible. What is not defensible is using the shortcut on one platform without noticing, and then reading a difference in results as a product bug. ## What "one suite" should actually mean One suite means one set of test intentions and one implementation of the steps, executed against two browsers whose sessions are configured separately and honestly. It does not mean one capability block. A team that insists on the second ends up with a lowest-common-denominator configuration that uses none of the Apple-side preferences, hides the Android bridge behind a helper nobody can debug, and still has to explain why the two platforms fail differently. Take the divergence on the chin at the configuration layer, and keep the sharing where it genuinely pays: above the session, in the steps and the assertions.
- Why not put the appium:safari keys in the shared block and let Android ignore them?Because "it will be ignored" is an assumption about another driver's handling of an unknown key, and it hides a real behavioural difference: only the iOS run gets that behaviour. Keeping the key in the iOS block costs nothing, makes the divergence visible in review, and stops anyone reading the shared block as a parity guarantee.
- How do you keep this configuration honest as the platforms move?Pin the ask rather than a version number, keep each platform's block small enough to read in one screen, and let the assertions carry the parity claim. When a run diverges, the first question is whether the difference is in the shared steps or in a platform block — a structure that cannot answer that question quickly is the wrong structure.
saying these in an interview costs you the question
- Insists one capability block can serve both platforms unchanged
- Pins a browser or driver version number in shared configuration
- Assumes an unknown capability is silently ignored by the other driver
- Asserts appium:nativeWebTap also tunes the Android Chrome session
- Measures success by identical setup rather than identical outcomes