BrowserStack
E2E.BrowserStack is a hosted device provider for the mobile engine. It ports upstream’s @e2e-dev/browserstack (docs/integrations/browserstack.mdx). It leases one BrowserStack App Automate real Android or iOS device for each worker slot when the run starts, and releases it when the run ends.
No Mac, Xcode, or Android SDK is needed on the machine that runs the tests.
Install and authenticate
Section titled “Install and authenticate”dotnet add package E2E.Mobile.NUnit # or E2E.Mobile.XUnit.V3dotnet add package E2E.BrowserStackSet BROWSERSTACK_USERNAME and BROWSERSTACK_ACCESS_KEY in the environment that starts the tests. In CI, store them as secrets. Copy them from Account & Profile, My profile, Authentication & Security. The username is the one shown there, not your email address. Without either, the lease fails before anything starts. Your plan needs App Automate. BROWSERSTACK_API_ENDPOINT reaches another API.
The test runner needs the .NET SDK and Node 22.12 or newer.
Configure a target
Section titled “Configure a target”A test class that derives from MobileAppTest (see Mobile) builds its MobileOptions in CreateMobileOptions(IDeviceProvider? provider). Set Provider to a BrowserStackDevices:
using E2E.BrowserStack;using E2E.Mobile;
const string apk = "https://www.browserstack.com/app-automate/sample-apps/android/WikipediaSample.apk";
var options = new MobileOptions{ Platform = MobilePlatform.Android, Provider = new BrowserStackDevices(new BrowserStackDevicesOptions { Device = "Google Pixel 7", OsVersion = "13.0", App = apk, AppiumVersion = "3.2.0", }), App = new MobileApp { BundleId = "org.wikipedia.alpha" },};Device and OsVersion must match BrowserStack’s App Automate catalog exactly, as the device list shows them: 13.0 on Android, 18 on iOS.
App is the build BrowserStack installs on every session. It is one of:
- a
bs://app id from an earlier upload; - an
httpsURL, which BrowserStack fetches once per run; - a local path, resolved against the project root and uploaded.
Use an .apk or .aab on Android and a signed .ipa on iOS. Leave the target’s App.AppPath out, and keep App.BundleId.
Set AppiumVersion. Without it, BrowserStack runs its default Appium 1.x, which lacks the mobile: commands that deep links and hardware buttons use.
Test an app flow
Section titled “Test an app flow”Tests are the same as on a local device:
using E2E;using E2E.Mobile.NUnit;using NUnit.Framework;
public sealed class HomeTests : MobileAppTest{ [Test] public async Task Wikipedia_home_screen() { await Expect.That(Screen.GetByText("Search Wikipedia")).ToBeVisibleAsync(); }}Choose device options
Section titled “Choose device options”BrowserStackDevicesOptions sets:
| Option | Default | Purpose |
|---|---|---|
Device |
required | Device name as the App Automate catalog lists it ("Google Pixel 7", "iPhone 16"). |
OsVersion |
required | OS version as the catalog lists it for that device. |
App |
required | The build BrowserStack installs: a bs:// app id, an https URL, or a local path. |
Project |
e2e |
Dashboard project the sessions are grouped under. |
Build |
the run id | Dashboard build the sessions are grouped under. |
SessionName |
e2e-<run id>-<target>-<slot> |
Session name on the dashboard. With more than one worker slot, -<slot> is appended. |
Orientation |
the device’s | BrowserStackOrientation.Portrait or BrowserStackOrientation.Landscape. |
GeoLocation |
BrowserStack’s | Country code the device’s IP geolocates to ("US"). |
Timezone |
the device’s | Time zone ("New_York"). |
Language |
the app’s | Language ("fr"). |
Locale |
the device’s | Locale ("fr_FR"). |
AppiumVersion |
BrowserStack’s (1.x) | Appium version for the session ("3.2.0"). |
NetworkProfile |
none | A named network condition profile ("4g-lte-advanced-good"). Not with CustomNetwork. |
CustomNetwork |
none | A custom network shape. Not with NetworkProfile. |
NoResignApp |
false |
iOS only. Keep the uploaded app’s signature instead of BrowserStack’s re-signing, which strips entitlements such as push notifications. |
Device features are set when the session starts and hold for the whole session. A blank value or a bad combination fails with INVALID_CONFIG when the provider is built. Catalog values and platform-specific constraints are checked when the device lease starts.
A test attempt that records video links BrowserStack’s recording of the session, found by its Build and SessionName. The recording covers the whole session, so every attempt on a worker slot links the same video. Runs that overlap and share a fixed Build and SessionName can link each other’s videos. The defaults never do.
Limits
Section titled “Limits”- Each session starts when its slot’s device is leased, while the engine prepares the target. Provisioning takes about 20 to 50 seconds. BrowserStack bills the session until the run releases it, even when no test runs on it. Keep the number of workers times the number of BrowserStack targets within your plan’s parallel sessions.
- BrowserStack keeps an upload for 30 days after its last use. A run with an
httpsAppuploads it again. Reuse abs://id to skip that. - If the runner is killed before it releases a device, BrowserStack’s idle timeout ends the session after its last command.
- A release fails the run when agent-device cannot confirm that it ended the session, even when every test passed. The causes are a session end that ran past agent-device’s shutdown window of about 3 seconds, a forced daemon stop, a lost shutdown report, or a daemon that exited during the run. Find the session on the dashboard by its
BuildandSessionName. - Device settings (permissions, location, network, appearance), system alerts, recording and device logs through agent-device, and port reverse are not available. Read logs on the BrowserStack dashboard, and use BrowserStack Local to reach a server on your network.
e2e exploreande2e mcpcannot lease a BrowserStack device. Set the provider in code.- This port is not verified against a live BrowserStack account. See COMPATIBILITY.md.