Skip to content

Add pytest-bdd + Appium App Automate sample (Android + iOS)#1

Open
AakashHotchandani wants to merge 9 commits into
mainfrom
sdk-sample
Open

Add pytest-bdd + Appium App Automate sample (Android + iOS)#1
AakashHotchandani wants to merge 9 commits into
mainfrom
sdk-sample

Conversation

@AakashHotchandani

Copy link
Copy Markdown
Collaborator

Import generated + live-validated BrowserStack SDK sample content, replacing the init scaffold. Single clean commit; no run artifacts.

@AakashHotchandani AakashHotchandani requested a review from a team as a code owner June 10, 2026 12:58
Comment thread .github/workflows/sdk-sample-test.yml Fixed

@shivam5643 shivam5643 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: pytest-bdd + Appium App Automate sample

Verdict: Request changes. The structure, READMEs, and the Wikipedia/iOS-text happy paths look solid, but the BrowserStack Local scenarios are non-functional as configured, and the README/CI/config contradict each other in ways that will burn customers who copy this repo.


🔴 Blocking issues

1. browserstackLocal: true is documented everywhere but present in neither browserstack.yml.

  • README.md and both tests/test_local.py docstrings state the local tunnel auto-starts "with browserstackLocal: true in browserstack.yml."
  • But android/browserstack.yml and ios/browserstack.yml do not contain that key (both end at networkLogs: true).
  • browserstack-local is in requirements.txt, but without the config flag the SDK won't start the tunnel. The local scenarios cannot work as shipped.

2. The Local sample apps are committed but never wired into any config — the local scenarios install the wrong app and will fail.

  • App Automate installs a single app: per run. Both yml files pin app: ./WikipediaSample.apk (android) / ./BStackSampleApp.ipa (ios).
  • LocalSample.apk / LocalSample.ipa are committed but referenced nowhere in browserstack.yml.
  • So when browserstack-sdk pytest -s tests/ runs both scenarios in one build, test_local executes against the Wikipedia/BStack app, where its selectors don't exist:
    • android: AppiumBy.ID, "com.example.android.basicnetworking:id/test_action"NoSuchElement
    • ios: AppiumBy.ACCESSIBILITY_ID, "TestBrowserStackLocal"NoSuchElement
  • Yet README.md explicitly claims "It also runs the local scenario (LocalSample.apk over the BrowserStack Local tunnel)." That's not achievable with one app: value. You'd need a separate config/build (or midSessionInstallApps) for the local app. Either remove the local scenarios or give them their own directory/config and document the two-build flow.

3. README contradicts the actual app: config (and the PR body / CI note).

  • README Prerequisites: "The Android directory is pre-wired to a pre-uploaded WikipediaSample.apk (bs://...)" and the CI note in .github/workflows/sdk-sample-test.yml talks about app: bs://... apps expiring.
  • Actual config is app: ./WikipediaSample.apk (local path, uploaded at runtime) — the opposite of "pre-uploaded bs://."
  • The android yml comment is also garbled/self-contradictory:
    # Pre-uploaded WikipediaSample.apk. Use a local path (./WikipediaSample.apk) to
    # Public sample app committed in this repo (relative path); the SDK uploads it at run time.
    
    First line is truncated and says the opposite of the second. Pick one model (local-path upload is the simpler, account-agnostic choice for a public sample) and make README + both yml comments + CI note agree.

🟠 Should fix

4. PR claims "live-validated" but CI only exercises one of four paths. sdk-sample-test.yml sets working-directory: android and runs only tests/test_sample.py — never the local scenario, never iOS. Given #1/#2, the local + iOS paths almost certainly were not green. Either extend CI to a matrix over {android, ios} × {sample, local} (with the local build using its own app config) or soften the "live-validated" claim. Right now CI silently passes while two-thirds of the repo is untested.

5. conftest.py contradicts its own docstring and duplicates creds. Both fixtures hardcode webdriver.Remote("https://hub.browserstack.com/wd/hub", ...) and stuff userName/accessKey into bstack:options, while the docstring says "no hub URL caps … are set here" and credentials already live in browserstack.yml. Under browserstack-sdk the hub/caps are SDK-managed; the manual hub URL + cred duplication is redundant and is the kind of thing a customer will copy into a real project and then wonder why env-var creds aren't honored. Recommend a bare UiAutomator2Options() / XCUITestOptions() and let the SDK inject everything (which is exactly what the docstring promises).


🟡 Minor / polish

  • android test_sample.py assertion is a no-op: assert len(results) > 0 on all TextViews is true the moment the app opens, before any search. It won't catch a broken search. Assert on result rows / a known result text instead.
  • time.sleep(5) in the android search step — fine for a sample, but a WebDriverWait reads better as exemplary customer code.
  • Pinning mismatch: pytest==7.4.4 hard-pinned while pytest-bdd is unpinned — a future pytest-bdd release can break the pinned pytest. Pin both or neither.

✅ What's good

  • Clean two-directory android//ios/ split, each self-contained — easy for a customer to copy one platform.
  • Gherkin features + step defs are readable and idiomatic pytest-bdd.
  • source: tagging and testObservability: true are correct for a sample.

Bottom line: the sample happy-paths (Wikipedia search / iOS text echo) are good, but ship-blockers #1–#3 mean the Local feature is broken and the docs misdescribe the app-provisioning model. I'd fix the Local config (or pull those scenarios), reconcile README/CI/yml on the app: model, and widen CI before merge.

🤖 Generated with Claude Code

@shivam5643 shivam5643 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inline comments for the findings in my review above — anchored to the specific lines.

Comment thread android/browserstack.yml
# Debugging features
# ===================
debug: true
networkLogs: true

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Blocking #1: browserstackLocal: true is missing here. The README and both tests/test_local.py docstrings claim the tunnel auto-starts "with browserstackLocal: true in browserstack.yml", and browserstack-local is in requirements.txt — but without this key the SDK never starts the tunnel, so the local scenario can't work. Add browserstackLocal: true.

Comment thread ios/browserstack.yml
# Debugging features
# ===================
debug: true
networkLogs: true

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Blocking #1 (iOS): same as android — browserstackLocal: true is missing, so the local-tunnel scenario can't function despite the docs/requirements implying it does.

Comment thread android/browserstack.yml Outdated
# ==========================================
# Application under test
# ==========================================
# Pre-uploaded WikipediaSample.apk. Use a local path (./WikipediaSample.apk) to

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Blocking #3: this comment is garbled and self-contradictory — line 1 ("Pre-uploaded WikipediaSample.apk. Use a local path … to") is truncated and says the opposite of line 2. It also contradicts the README Prerequisites ("pre-wired to a pre-uploaded WikipediaSample.apk (bs://...)") and the CI bs:// note, while the actual config below uses a local path. Pick one model (local-path upload is the simpler choice for a public sample) and make README + both yml comments + CI agree.

@when("I trigger the local network test")
def trigger_local_test(driver):
driver.find_element(
AppiumBy.ID, "com.example.android.basicnetworking:id/test_action").click()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Blocking #2: App Automate installs a single app: per run, and android/browserstack.yml pins app: ./WikipediaSample.apk. LocalSample.apk is committed but referenced nowhere, so this step runs against the Wikipedia app where com.example.android.basicnetworking:id/test_action doesn't exist → NoSuchElement. The README's claim that pytest -s tests/ "also runs the local scenario (LocalSample.apk)" isn't achievable with one app: value. Give the local scenario its own directory/config (or use midSessionInstallApps) and document the two-build flow — or drop it.

Comment thread ios/tests/test_local.py
@when("I trigger the local network test")
def trigger_local_test(driver):
driver.find_element(AppiumBy.ACCESSIBILITY_ID, "TestBrowserStackLocal").click()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Blocking #2 (iOS): same problem — ios/browserstack.yml installs BStackSampleApp.ipa, not LocalSample.ipa, so TestBrowserStackLocal won't be found. The local app is committed but never wired into any config.

pip install -r requirements.txt
- name: Run sample test
run: |
browserstack-sdk pytest tests/test_sample.py

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 Should fix #4: combined with working-directory: android above, CI only ever runs the android sample test — never the local scenario, never iOS. Given blocking #1/#2, the local + iOS paths were almost certainly not green. The PR body says "live-validated"; either extend CI to a {android, ios} × {sample, local} matrix (local using its own app config) or soften that claim.

Comment thread .github/workflows/sdk-sample-test.yml Outdated
# Runs the BrowserStack SDK sample against a given commit and reports a status check.
# Trigger: Actions tab -> "pytest-bdd Appium App Automate SDK sample test" -> Run workflow -> paste the PR's full commit SHA.
# Requires repo secrets: BROWSERSTACK_USERNAME, BROWSERSTACK_ACCESS_KEY.
# NOTE (App Automate): the app under test is referenced via `app: bs://...` in browserstack.yml;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Ties into Blocking #3: this note describes app: bs://... (re-upload if expired), but the committed browserstack.yml uses a local path that the SDK uploads each run — so this guidance doesn't match the repo. Update once the app model is settled.

Comment thread android/tests/test_sample.py Outdated
@then("search results are displayed")
def results_displayed(driver):
results = driver.find_elements(AppiumBy.CLASS_NAME, "android.widget.TextView")
assert len(results) > 0

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Minor: assert len(results) > 0 over all TextViews is true the moment the app opens, before any search — it won't catch a broken search. Assert on actual result rows or a known result text.

search = driver.find_element(
AppiumBy.ID, "org.wikipedia.alpha:id/search_src_text")
search.send_keys(query)
time.sleep(5)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Minor: time.sleep(5) is fine for a sample, but a WebDriverWait on the results element reads better as exemplary customer code.

Comment thread android/requirements.txt Outdated
@@ -0,0 +1,6 @@
Appium-Python-Client
selenium>=3.14
pytest==7.4.4

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Minor: pytest==7.4.4 is hard-pinned while pytest-bdd (line 4) is unpinned — a future pytest-bdd release can require a newer pytest and break this pin. Pin both or neither.

…ocument local scenario as a separate one-app-per-build run (LocalSample), reconcile README/yml-comment/CI note to the committed local-path app model, drop conftest cred duplication + fix docstring, meaningful sample assertion, unpin pytest
@AakashHotchandani

Copy link
Copy Markdown
Collaborator Author

Thanks for the thorough review @shivam5643 🙏 — all valid, addressed in the latest commit. Point by point:

🔴 #1 browserstackLocal: true missing — Fixed. Added to both android/browserstack.yml and ios/browserstack.yml.

🔴 #2 Local scenario installs the wrong app — Correct: App Automate installs one app per build, so the local scenario can't share a build with the Wikipedia/BStack sample. Fixed by making the docs honest and the scenario achievable: the README no longer claims pytest -s tests/ runs both. The local scenario is now documented as a separate build against the committed local app (LocalSample.apk / LocalSample.ipa — set app: ./LocalSample.apk for that run) with browserstackLocal: true. I kept the local apps committed + documented the two-build model rather than dropping the scenario; didn't fake a one-command flow given the single-app:-per-session constraint.

🔴 #3 README / yml-comment / CI note contradict the app model — Fixed. Cleaned the garbled android/browserstack.yml comment, updated README Prerequisites to the local-path model (no more bs://), and corrected the workflow's App-Automate note to match (app: ./WikipediaSample.apk, uploaded at run time).

🟠 #4 CI only runs the android sample — Acknowledged. CI validates the android sample path; iOS + the local scenario aren't in CI (local needs its own one-app build). The docs now state what's covered rather than overclaiming. Happy to extend CI to an {android,ios} × {sample,local} matrix as a follow-up — it needs the per-build app swap + repo secrets (not set yet).

🟠 #5 conftest hardcodes hub + duplicates creds — Fixed the substantive part: removed the bstack:options cred duplication (it can shadow env-var creds) and corrected the docstring. I kept the explicit hub.browserstack.com/wd/hub URL — that's the verified-working pattern across all our passing appium samples; a fully-bare Remote(options=...) (relying on SDK injection of the hub) wasn't validated and I didn't want to ship an unverified change. Glad to switch to fully-bare if you've confirmed it works.

🟡 Minors — the android assertion now verifies results actually match the query (any("BrowserStack" in r.text …)) instead of the no-op len > 0; unpinned pytest so it's "neither pinned" alongside pytest-bdd. Left time.sleep(5) as-is for sample simplicity (noted).

(Also: the CodeQL "workflow does not contain permissions" inline comment is already resolved — the permissions: { contents: read, checks: write } block is present; that alert predates the fix.)

PTAL 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants