use-case· 16 min read· by Pramod Dutta

AI Testing for Mobile Web & Responsive Layouts

AI testing for mobile web: verify responsive breakpoints and mobile menus by stating intent, with BrowserBash driving real Chromium at set viewports.

Most "mobile" bugs your users hit are not native bugs at all. They are responsive web bugs: a navigation menu that won't open below 768px, a checkout button that slides under the keyboard on a 390px screen, a pricing table that overflows horizontally on a small phone. AI testing for mobile web is about catching those layout and interaction failures without standing up a device farm. This article shows how to verify responsive breakpoints and mobile menus by stating your intent in plain English, while BrowserBash drives a real Chromium browser at a set viewport, returns a verdict, and gives you a lighter path than maintaining an Appium rig or a BrowserStack device matrix for web flows.

The argument here is narrow on purpose. If you ship a React Native app or a hybrid WebView, you need real device automation, and nothing in this post replaces that. But a huge share of mobile QA is just your website rendered at phone width. For that work, you do not need a capabilities object or a paid device cloud. You need a browser that pretends to be a phone and a fast way to assert the flow actually works. Let's walk through how to do that, where the approach earns its keep, and where it genuinely does not.

Why responsive web testing is its own problem

Desktop and mobile share the same codebase, but they fail in different places. Your CSS has breakpoints — the widths where the layout reflows from a multi-column desktop grid into a single stacked mobile column. Around those breakpoints, things break: a flex container that doesn't wrap, a sticky header that eats the viewport, a modal that becomes unscrollable, a touch target that's two pixels too small. None of that shows up in a desktop run at 1440px wide.

The classic answer was to test on real devices. That gives you the truest rendering, but it is slow, expensive, and noisy. You wait on emulators to boot or on a remote device to free up in a shared pool. You debug flakiness that has nothing to do with your app and everything to do with the harness. And for most responsive bugs, the device fidelity is overkill — the bug reproduces the moment you shrink a Chromium window to 375px and try to tap the hamburger.

The middle path is viewport emulation: run a real browser engine, set the viewport to a known phone size, emulate touch and the mobile user agent, and exercise the flow. This is exactly what Chrome DevTools' device toolbar does, and it catches the overwhelming majority of layout-and-reflow defects. BrowserBash leans into this path. It drives real Chromium locally, so you get a genuine rendering engine, but you describe the test as an objective instead of writing selectors against a DOM that changes every sprint.

What viewport emulation catches — and what it misses

Be honest about the boundary, because credibility matters more than the pitch. Viewport emulation in Chromium reliably catches:

It does not perfectly reproduce:

If those are your highest-risk failure modes — say you've been burned repeatedly by iOS Safari — keep a real-device check in the loop. For everything else, emulated Chromium at the right viewport is faster, cheaper, and catches the bug before it ships.

What BrowserBash is, in one paragraph

BrowserBash is a free, open-source (Apache-2.0) command-line tool from The Testing Academy. You install it with npm install -g browserbash-cli, then write a plain-English objective. An AI agent drives a real Chrome or Chromium browser step by step — no selectors, no page objects, no waits to tune — and returns a pass/fail verdict plus structured results. It is Ollama-first: by default it uses free local models, so no API keys are required and nothing leaves your machine. It can also resolve to an Anthropic key or an OpenRouter key if you set one, including genuinely free hosted models such as openai/gpt-oss-120b:free. You can run the whole thing at a $0 model bill on local models. There's a deeper tour on the BrowserBash learn pages if you want the full surface.

One honest caveat to set expectations: very small local models (roughly 8B parameters and under) can get flaky on long, multi-step objectives. The sweet spot for mobile-web flows is a mid-size local model in the Qwen3 or Llama 3.3 70B class, or a capable hosted model for the hardest journeys. Keep your objectives focused and that flakiness mostly disappears.

Setting a mobile viewport and stating your intent

The core move for responsive testing is to fix the viewport, then describe what should be true. Because you write intent rather than mechanics, your test reads like an acceptance criterion someone on your team actually wrote:

browserbash run "On https://shop.example.com at a 390x844 mobile viewport, \
tap the hamburger menu in the top-right, confirm the navigation drawer \
slides open and shows links for Home, Shop, and Account, then close it \
and verify the drawer is hidden again." --record

The --record flag captures a screenshot and a full .webm session video via ffmpeg, so when a mobile menu fails to open you have a recording of exactly what the browser did. That matters for responsive bugs, because "the menu didn't open" is much easier to triage when you can watch the tap land and nothing happen.

There's nothing magic about 390x844 — it's the iPhone-class portrait size. Swap in 360x800 for a common Android width, 768x1024 for tablet portrait, or your own breakpoint boundaries. The point is to pin the viewport to a width you care about and let the agent exercise the layout there.

Verifying breakpoints, not just one width

A single phone width tells you the mobile layout works. It doesn't tell you the transition works. Responsive bugs love the edges — the few pixels around a breakpoint where the layout decides whether to stack or stay side-by-side. A useful pattern is to run the same objective at three widths that bracket your breakpoint: just below it, just above it, and a desktop control.

# Just below the 768px breakpoint — expect the stacked mobile layout
browserbash run "On https://example.com at a 760x1024 viewport, confirm the \
nav collapses to a hamburger icon and the hero text stacks above the image." --headless

# Just above the breakpoint — expect the desktop layout
browserbash run "On https://example.com at a 800x1024 viewport, confirm the \
full horizontal nav is visible and the hero text sits beside the image." --headless

Running --headless keeps these fast and CI-friendly. When both pass, you have evidence the breakpoint flips cleanly in both directions, which is the bug class most viewport tests forget to cover.

Committable mobile tests with Markdown

For tests you want to keep, review, and run in CI, BrowserBash supports Markdown test files. A *_test.md file treats each list item as a step, supports @import for composing shared setup, and {{variables}} for templating. Variables marked as secrets are masked as ***** in every log line, which keeps credentials out of your CI output. Each run also writes a human-readable Result.md.

Here's a mobile-menu-and-login flow as a committable test:

# mobile_login_test.md

- Open https://app.example.com at a 390x844 mobile viewport
- Confirm the desktop nav is collapsed into a hamburger icon
- Tap the hamburger menu and confirm the drawer opens
- Tap "Sign in" inside the drawer
- Enter {{username}} into the email field
- Enter {{password}} into the password field
- Tap the "Log in" button
- Verify the account dashboard loads and the drawer is closed

Run it like this:

browserbash testmd run ./mobile_login_test.md \
  --var username=qa@example.com \
  --secret password=hunter2

Because these files live in your repo, a reviewer can read the mobile test in a pull request the same way they'd read a spec. There's no page-object layer to keep in sync with the markup, which is the maintenance cost that quietly kills most mobile-web suites. The full Markdown test format is covered in the BrowserBash feature docs.

Wiring mobile-web checks into CI

The point of agent mode is that machines, not humans, read the output. Add the --agent flag and BrowserBash emits NDJSON — one JSON event per line on stdout — plus meaningful exit codes: 0 passed, 1 failed, 2 error, 3 timeout. No prose parsing, no scraping a log for the word "PASS."

browserbash run "At a 360x800 mobile viewport on https://example.com, \
confirm the cookie banner is dismissible, the hamburger menu opens, \
and the footer links are reachable by scrolling." --agent --headless

In a CI step, the exit code gates your pipeline directly: a non-zero code fails the build, and the NDJSON stream gives you a structured, per-step record to attach as an artifact. This is also the contract that lets an AI coding agent call BrowserBash as a tool — it gets back JSON it can reason about, not text it has to interpret. If you want run history, video replay, and per-run timelines in a UI, the optional free cloud dashboard is strictly opt-in via browserbash connect and --upload, with free uploaded runs retained for 15 days. Prefer to keep everything local? browserbash dashboard gives you a fully local dashboard with no upload at all.

The comparison: BrowserBash vs Appium vs BrowserStack for mobile web

This is where the honest trade-offs live. All three can tell you whether your responsive site works on a phone-shaped screen, but they cost very different amounts of setup and money, and they have different fidelity ceilings.

Dimension BrowserBash (emulated Chromium) Appium BrowserStack / device cloud
What it drives Real Chromium at a set viewport, locally Real mobile browser on emulator or device Real browsers on real/virtual devices in the cloud
Setup cost npm install -g browserbash-cli Appium server + driver + emulator/SDK Account + credentials + cloud config
How you write a test Plain-English objective, no selectors Code against WebDriver + capabilities Code against your framework + cloud caps
iOS Safari fidelity Not exact (Chromium, not WebKit) High (real Safari) High (real Safari)
Real touch / device quirks Emulated only Real Real
Cost model Free, OSS; $0 on local models Free, OSS (infra is on you) Paid plans (as of 2026; check current pricing)
Maintenance Low (no page objects) Higher (drivers, selectors, capabilities) Higher (selectors + cloud config)
Best fit Responsive web flows, breakpoints, menus Native + hybrid apps, true device runs Broad real-device coverage matrix

A few notes so this table isn't misleading. Appium is an OpenJS Foundation project that speaks the W3C WebDriver protocol; its real strength is native and hybrid apps, and it can drive actual mobile Safari and Chrome — fidelity BrowserBash's emulation does not match. BrowserStack and similar device clouds give you a genuine matrix of real devices and browsers, which is exactly what you want when you must certify against a long list of physical hardware. Their specific pricing and device inventory change over time and aren't worth my fabricating here; check their current plans directly. The point of BrowserBash is not to beat those tools on device fidelity. It's to remove the setup and maintenance tax for the large middle of mobile QA that is really just responsive web.

When BrowserBash is the better fit

Reach for emulated-Chromium AI testing for mobile web when:

When Appium or a device cloud wins

Be equally clear about where you should not use this approach:

If your suite is mostly the second list, this article isn't for you, and that's fine. Most teams are a blend: a thin layer of real-device checks for the scary platform-specific cases, and a much larger layer of fast emulated-web checks for everyday responsive coverage. BrowserBash is aimed squarely at that larger layer.

A realistic mobile checkout walkthrough

Let's make this concrete with a flow most teams actually have: a mobile web checkout. The objective spans several steps — open a store, add an item, complete checkout, and confirm the order — all at phone width.

browserbash run "On https://demo-store.example.com at a 390x844 mobile \
viewport: open the menu, navigate to a product, add it to the cart, open \
the cart drawer, proceed to checkout, fill the test card details, place the \
order, and verify the page shows 'Thank you for your order!'." \
  --record --headless

A few things are worth calling out about how this behaves in practice. First, this is a long multi-step objective, which is exactly where small local models struggle — run it on a mid-size local model or a capable hosted one and it holds together far better. Second, --record gives you the .webm plus a screenshot, so if checkout fails on the narrow viewport you can watch where the layout trapped the agent — a submit button under the sticky footer, a field the keyboard covered, a drawer that wouldn't scroll. Third, if you use the builtin engine instead of the default Stagehand engine, recording also captures a Playwright trace you can open in the trace viewer, which is the most detailed view when you need to debug a specific failed step.

The deeper value is that this same objective is portable across providers. By default the browser runs locally on your Chrome. Switch where the browser runs with a single --provider flag — cdp to point at any DevTools endpoint, or browserbase, lambdatest, or browserstack to run on their infrastructure when you do want a wider device or browser spread:

browserbash run "At a 390x844 mobile viewport, complete the checkout flow \
on https://demo-store.example.com and verify the order confirmation." \
  --provider lambdatest

You wrote the test once in plain English. Running it locally for fast feedback and on a cloud provider for broader coverage is a flag change, not a rewrite. That's a meaningfully different maintenance story from keeping parallel Appium and cloud-framework suites in sync.

Keeping mobile-web tests stable

Selector-free testing removes the most common source of mobile-suite rot — brittle CSS and XPath selectors that break when a designer renames a class — but it doesn't make tests magically immortal. A few habits keep responsive runs reliable.

Keep objectives scoped. "Open the menu and confirm three links appear" is a crisp, checkable step. "Test the entire site on mobile" is not, and it's where flakiness creeps in. Smaller objectives also localize failures: when one fails, you know precisely which interaction broke.

Pin the viewport explicitly in the objective. Don't assume a default; state "at a 390x844 mobile viewport" so the test is self-documenting and reproducible. A reviewer reading the Markdown test should know exactly what screen size you meant.

Assert on user-visible truth, not implementation. "The navigation drawer is visible and shows Home, Shop, Account" survives a markup refactor. "The element with class .nav-drawer--open exists" does not, and it's the kind of coupling this approach is meant to avoid.

Use secrets for credentials. The {{variable}} masking turns a password into ***** in every log line, including the Result.md and any uploaded run, so you can test authenticated mobile flows without leaking anything into CI output. There are more examples of this in the BrowserBash case studies and across the broader BrowserBash blog.

Choosing a model for mobile flows

Because model choice drives reliability on multi-step objectives, it's worth being deliberate. For short single-screen checks — does the menu open, does the form fit — a small local model is often fine and costs nothing. For long journeys like the checkout walkthrough above, move to a mid-size local model (Qwen3 or Llama 3.3 70B-class) or a capable hosted model. The auto-resolution order is local Ollama first, then ANTHROPIC_API_KEY, then OPENROUTER_API_KEY, so you can default to free local runs and only reach for a hosted model on the hard flows. OpenRouter even exposes genuinely free hosted models if you'd rather not run anything locally. You can read more about the model and provider options on the pricing page.

Putting it together: a sensible mobile testing layer

A pragmatic responsive-testing setup with BrowserBash looks like this. You write a handful of committable *_test.md files covering your critical mobile journeys — menu open/close, login, the one or two flows that pay your bills like checkout or sign-up. You parameterize them with {{variables}} and mark credentials as secrets. You run them in CI with --agent --headless, gating the build on exit codes and saving the NDJSON and .webm recordings as artifacts. You run the same files at two or three viewport widths to cover the breakpoint transition, not just one phone size.

Then you keep a small, deliberate set of real-device checks — on Appium or a device cloud — for the platform-specific risks emulation can't see. That's the whole strategy: cheap, fast, plain-English emulated coverage for the bulk of responsive QA, and real devices reserved for the cases that actually need them. You stop paying the device-matrix tax for tests that never needed a device in the first place.

FAQ

Can AI testing for mobile web replace real device testing?

For most responsive-web bugs — broken breakpoints, hamburger menus that won't open, layouts that overflow on small screens — emulated Chromium at a set viewport catches them faster and cheaper than a device farm. It does not perfectly reproduce iOS Safari-specific rendering or real touch physics, so keep a small set of real-device checks for those platform-specific risks. Think of emulation as the bulk layer and real devices as the targeted safety net.

How do I set a mobile viewport in BrowserBash?

State the viewport directly in your plain-English objective, for example "at a 390x844 mobile viewport." BrowserBash drives real Chromium at that size, emulating a phone-shaped screen so the responsive layout reflows the way it would on a device. You can pick any width and height, and a useful pattern is to run the same test just below and just above a CSS breakpoint to confirm the transition works in both directions.

Is BrowserBash a good Appium alternative for mobile web?

For mobile web flows — your responsive website, a PWA, a mobile checkout — BrowserBash removes the Appium setup tax: no server, no driver, no capabilities object, and no selectors to maintain. For genuine native or hybrid apps, Appium is still the right tool because it automates the actual mobile app and real mobile browsers. Many teams use BrowserBash for the responsive-web bulk and keep Appium focused on native work.

Does running mobile tests with BrowserBash cost anything?

No. BrowserBash is free and open-source under Apache-2.0, and it's Ollama-first, so it defaults to free local models with no API keys and nothing leaving your machine — a genuine $0 model bill. If you want a capable hosted model for the hardest multi-step flows, you can bring an Anthropic key or use OpenRouter, including some genuinely free hosted models. The optional cloud dashboard for run history and video replay is also free and strictly opt-in.

Responsive bugs hide in the few pixels around your breakpoints and in menus that only fail at phone width. You can find them without a device farm: pin a mobile viewport, state what should be true, and let a real Chromium browser do the rest. Install with npm install -g browserbash-cli and run your first mobile-web check in a couple of minutes. No account is required to run locally; if you want run history and video replay, sign up for the free dashboard at browserbash.com/sign-up.

Try it on your own appnpm install -g browserbash-cli
Start learning