Skip to content

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.

Terminal window
dotnet add package E2E.Mobile.NUnit # or E2E.Mobile.XUnit.V3
dotnet add package E2E.BrowserStack

Set 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.

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 https URL, 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.

Tests are the same as on a local device:

HomeTests.cs
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();
}
}

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.

  • 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 https App uploads it again. Reuse a bs:// 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 Build and SessionName.
  • 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 explore and e2e mcp cannot lease a BrowserStack device. Set the provider in code.
  • This port is not verified against a live BrowserStack account. See COMPATIBILITY.md.