How to Test Cookie Consent on Android (Complete Guide)
Testing cookie consent on Android is not a niche activity; it is a core part of delivering a compliant, trustworthy app. When a user opens an application that loads web content—whether through a WebVi
Introduction
Testing cookie consent on Android is not a niche activity; it is a core part of delivering a compliant, trustworthy app. When a user opens an application that loads web content—whether through a WebView, Chrome Custom Tab, or a hybrid framework—the app must present a clear mechanism for accepting, rejecting, or managing cookies before any tracking scripts run. Failure to do so can lead to regulatory fines, loss of‑fines under GDPR, CCPA, or ePrivacy directives, erode user confidence, and break analytics pipelines that depend on consent‑gated data.
This guide walks you through why consent matters, how Android apps typically implement consent banners, a comprehensive test matrix covering happy paths, error conditions, accessibility, and security, step‑by‑step manual procedures, automated strategies with Espresso, UI Automator, Appium, and a look at autonomous persona‑driven exploration. Each section contains concrete commands, code snippets, and tables you can copy into your test repository. By the end you will have a repeatable checklist that you can bookmark and apply to any Android release.
---
Why Cookie Consent Matters on Android
Legal foundations
The General Data Protection Regulation (GDPR) requires a freely given, specific, informed, and unambiguous indication of the user’s wishes before personal data—including cookies that identify a device—can be processed. The ePrivacy Directive (often called the “cookie law”) adds that consent must be obtained before storing or accessing information on a user’s terminal equipment. In the United States, the California Consumer Privacy Act (CCPA) grants consumers the right to opt‑out of the sale of personal information, which includes many tracking cookies.
Business impact
Non‑compliance can trigger fines of up to 4 % of global annual turnover under GDPR, and CCPA penalties can reach $7 500 per intentional violation. Beyond regulator action, users are increasingly aware of privacy cues; a study by the Pew Research Center found that 79 % of smartphone users abandon an app when they feel their data is being harvested without clear consent.
Technical risk
If a consent banner fails to block analytics or ad SDKs, those libraries may fire network requests before the user has made a choice. This can contaminate data sets, skew A/B test results, and expose the app to data leakage bugs that are hard to reproduce in a staging environment where network throttling or consent mocks are not applied.
---
How Android Apps Present Cookie Consent
WebView‑based banners
Many apps embed a WebView that loads a consent management platform (CMP) hosted by a third party (OneTrust, TrustArc, Cookiebot, etc.). The WebView renders HTML, CSS, and JavaScript exactly as a browser would, and the app typically injects a JavaScript interface to read the user’s choice and store it in SharedPreferences or a SQLite database.
Chrome Custom Tabs
When an app launches a Custom Tab to display a privacy policy or consent page, the banner lives inside the Chrome process. Communication back to the host app is handled via CustomTabCallback or by reading a URL fragment that contains the consent token after the user taps “Accept”.
Native dialogs mimicking web banners
Some teams build fully native consent screens using Android XML layouts. These dialogs appear as an AlertDialog, a bottom sheet, or a full‑screen activity. Although they avoid WebView complexity, they still need to communicate the decision to any embedded web views or analytics SDKs.
Third‑party SDKs with built‑in consent handling
SDKs such as Google AdMob, Facebook Audience Network, or Firebase Analytics expose APIs like ConsentInformation.updateConsentStatus(...). The app must call these APIs after the user interacts with the banner; otherwise the SDK may initialize with a default state that assumes consent.
---
Test Matrix for Cookie Consent on Android
The following table enumerates test cases that cover functional, accessibility, localization, and security dimensions. Each row can be copied into a test‑management tool (Zephyr, Xray, TestRail) and linked to automated scripts.
| Test ID | Category | Description | Preconditions | Steps | Expected Result | Pass/Fail Criteria |
|---|---|---|---|---|---|---|
| CC‑001 | Happy Path | Banner appears on first launch and accepts consent | Fresh app install, no stored consent | 1. Launch app 2. Observe banner 3. Tap “Accept” | Banner disappears, consent flag set to true, analytics SDK initializes | PASS if banner dismissed and SharedPreferences.getBoolean("consent_given", false) returns true |
| CC‑002 | Happy Path | Banner appears and rejects consent | Fresh app install | 1. Launch app 2. Observe banner 3. Tap “Reject” | Banner disappears, consent flag set to false, analytics SDK does not send payloads | PASS if banner dismissed and no network request to analytics endpoint observed |
| CC‑003 | Error Path | Banner fails to load due to missing CMP script | Device offline, CMP URL blocked by firewall | 1. Disable Wi‑Fi/mobile data 2. Launch app 3. Wait 10 s | App shows fallback message or proceeds without banner, no crash | PASS if app does not throw uncaught exception and logs a warning |
| CC‑004 | Error Path | Banner HTML contains malformed JavaScript causing WebView crash | CMP serves broken JS | 1. Enable network 2. Point CMP to a test server returning 3. Launch app | WebView stays alive, app shows error toast, consent not stored | PASS if app does not crash and error is captured |
| CC‑005 | Localization | Banner text respects device locale | Device set to French (fr‑FR) | 1. Set locale to French 2. Clear app data 3. Launch app | Banner displays French translation, buttons labeled “Accepter” / “Refuser” | PASS if all visible strings match fr‑FR resource file |
| CC‑006 | Localization | Right‑to‑left layout for Arabic | Device set to Arabic (ar‑SA) | 1. Set locale to Arabic 2. Clear app data 3. Launch app | Banner mirrors horizontally, text aligned right | PASS if layout direction is RTL and no clipping |
| CC‑007 | Orientation | Banner survives orientation change | Banner visible | 1. Launch app 2. Rotate device to landscape 3. Rotate back to portrait | Banner remains visible, consent state unchanged | PASS if banner is present in both orientations and no flicker |
| CC‑008 | Orientation | Banner reloads incorrectly on rotation | Banner uses android:configChanges="orientation" incorrectly | Same as CC‑007 but observe network | No duplicate network request to fetch banner | PASS if only one request occurs |
| CC‑009 | Low Memory | System kills WebView while banner is showing | Device with 2 GB RAM, background memory‑heavy app | 1. Start memory‑stress app 2. Launch target app 3. Wait for banner | App recovers gracefully, shows fallback or retains consent if already set | PASS if no NullPointerException from WebView and app stays responsive |
| CC‑010 | Network Latency | Banner delayed due to slow CMP response | Network throttled to 50 kbps | 1. Apply throttle via tc or Charles 2. Launch app 3. Wait up to 30 s | Banner eventually appears, user can interact | PASS if banner appears within 30 s and no timeout crash |
| CC‑011 | Consent Revocation | User can withdraw consent via settings | Consent previously given | 1. Launch app 2. Open Settings → Privacy → Manage Consent 3. Tap “Withdraw” 4. Restart app | Banner reappears on next launch, consent flag false | PASS if banner shown again and analytics disabled |
| CC‑012 | Multiple Layers | First‑party banner and third‑party SDK banner both present | App uses its own banner and loads an ad SDK that also shows consent | 1. Launch app 2. Observe both banners | Only the topmost banner is interactive; underlying SDK respects the decision | PASS if lower layer does not fire tracking calls before user choice |
| CC‑013 | Accessibility – TalkBack | Banner reachable via TalkBack | TalkBack enabled | 1. Enable TalkBack 2. Launch app 3. Swipe to focus | Focus lands on banner title, then each button, with correct labels | PASS if all elements are focusable and announce “Accept button, double tap to accept” |
| CC‑014 | Accessibility – Font Scale | Banner respects 200 % font scaling | Device font size set to Largest | 1. Set font size to Largest 2. Launch app | Banner text scales, layout does not truncate buttons | PASS if all text visible and buttons tappable |
| CC‑015 | Accessibility – Contrast | Banner meets WCAG AA contrast | Use a color analyzer | 1. Capture screenshot 2. Run contrast check | Text/background ratio ≥ 4.5:1 | PASS if ratio meets threshold |
| CC‑016 | Security – Cookie Attributes | Cookies set after consent have Secure and SameSite flags | Consent given, network sniffing enabled | 1. Accept consent 2. Capture HTTP responses with mitmproxy 3. Inspect Set‑Cookie headers | Each cookie includes Secure and SameSite=Strict or SameSite=Lax | PASS if all tracking cookies satisfy both attributes |
| CC‑017 | Security – No Sensitive Data in Cookie Value | Cookie does not contain email, token, PII | Same as CC‑016 | 1. Accept consent 2. Examine cookie value | Value is random identifier or empty | PASS if no recognizable PII appears |
| CC‑018 | Security – Consent Revocation Clears Cookies | Withdrawing consent removes or invalidates cookies | Consent given, cookies present | 1. Accept consent 2. Capture cookies 3. Withdraw consent via settings 4. Capture cookies again | No tracking cookies present or they are marked as expired | PASS if cookie jar is empty or all tracking cookies have Expires in the past |
| CC‑019 | Inter‑App Interaction | Launching Custom Tab from another app respects consent | Another app uses CustomTabIntent to open your consent page | 1. Set consent to false 2. From external app launch https://example.com/consent via Custom Tab 3. Observe behavior | Consent banner shown, user must act before proceeding | PASS if banner appears and no tracking occurs before interaction |
| CC‑020 | Ad Network Consent | Ad SDK does not initialize before consent | App integrates AdMob | 1. Launch app with consent false 2. Wait for ad request | No ad request sent to googleads.g.doubleclick.net | PASS if ad network stays idle until consent given |
*Notes:*
- Test IDs CC‑001 through CC‑005 cover the core functional flow.
- CC‑006‑CC‑008 address localization and orientation, which often expose layout bugs.
- CC‑009‑CC‑010 simulate resource‑constrained environments that are common in the field.
- CC‑011‑CC‑013 focus on user‑controlled consent lifecycle and accessibility.
- CC‑014‑CC‑020 dive into security, privacy, and cross‑app interactions that are frequently missed by scripted tests.
---
Manual Testing Approach
Setting up the environment
- Device or emulator – Use a Pixel 4 API 33 emulator for baseline tests and a physical device with Android 13 for real‑world validation.
- Clear app state –
adb uninstall com.example.app && adb install app-release.apkensures a clean install. - Network control – Install *Charles Proxy* or *mitmproxy* on your host machine, then point the device to the proxy via Wi‑Fi settings (hostname of host, port 8888). Enable SSL proxying to inspect HTTPS traffic.
- Accessibility tools – Turn on TalkBack (
Settings → Accessibility → TalkBack) and Switch Access if you need to validate alternative navigation. - Log capture – Run
adb logcat -v threadtime > logcat.txtbefore launching the app; filter later withgrep Consent.
Step‑by‑step manual checklist
| Step | Action | Observation | Tool |
|---|---|---|---|
| 1 | Install a fresh build | No prior consent stored | adb install |
| 2 | Launch the app | Consent banner appears within 2 s | Visual inspection |
| 3 | Verify banner text matches locale | Correct language, proper RTL if needed | Screenshot + diff |
| 4 | Enable TalkBack | Focus moves to banner title, then each button | TalkBack feedback |
| 5 | Change font size to Largest | Text scales, buttons remain tappable | Visual |
| 6 | Toggle dark mode | Banner colors adapt, contrast ≥ 4.5:1 | System settings |
| 7 | Rotate device | Banner persists, no duplicate network call | adb logcat + proxy |
| 8 | Simulate low memory | Run a memory‑hog (adb shell am broadcast -a com.example.MEMORY_HOG) | Observe no crash |
| 9 | Apply network throttling | Banner delayed but eventually loads | Charles throttle settings |
| 10 | Tap “Accept” | Banner disappears, consent flag true | adb shell settings get global consent_given or inspect SharedPreferences |
| 11 | Verify analytics endpoint not called before acceptance | No request to analytics host in proxy | mitmproxy filter |
| 12 | Tap “Reject” | Banner disappears, consent flag false, analytics idle | Same as 10/11 |
| 13 | Withdraw consent via settings | Banner reappears on next launch | Settings UI |
| 14 | Check cookie attributes | Each Set‑Cookie includes Secure and appropriate SameSite | mitmproxy → Response → Set‑Cookie |
| 15 | Inspect cookie value for PII | No email, token, or personal data | Manual scan |
| 16 | Close app, reopen | Consent state persisted across sessions | SharedPreferences check |
| 17 | Uninstall and reinstall | Fresh start shows banner again | adb uninstall + install |
Observing UI with TalkBack
When TalkBack is on, swipe right to move focus. The first spoken element should be the banner’s heading (e.g., “We use cookies to improve your experience”). Subsequent swipes should announce each action button with its label and state (“Accept button, not selected”). If any element is skipped or announces incorrectly, note the ID and fix the contentDescription or labelFor attribute.
Logging network traffic with Charles / mitmproxy
Configure the proxy to intercept *.doubleclick.net, *.google-analytics.com, and any custom endpoints. After each user action, filter the timeline for Set‑Cookie headers and POST/ GET requests. The presence of a request before consent indicates a failure in the consent‑gating logic.
Checking SharedPreferences / SQLite for stored consent
Most CMPs write a boolean flag:
adb shell run-as com.example.app cat shared_prefs/com.example.app_prefs.xml
Look for . If the flag is missing or incorrect after a user interaction, the app’s consent storage layer needs review.
---
Automated Testing Approaches
Espresso UI tests
Espresso works well for native consent screens and for interacting with WebView content when you enable WebView testing. Below is a concise test that validates the happy‑path accept flow.
@RunWith(AndroidJUnit4.class)
public class ConsentBannerTest {
@Rule
public ActivityTestRule<MainActivity> activityRule =
new ActivityTestRule<>(MainActivity.class, true, false);
@Test
public void acceptConsent_hidesBanner_andSetsFlag() {
// Launch with a clean state
activityRule.launchActivity(new Intent());
// Wait for banner to appear (assuming id consent_banner)
onView(withId(R.id.consent_banner))
.check(matches(isDisplayed()));
// Tap Accept
onView(withId(R.id.btn_accept))
.perform(click());
// Banner should be gone
onView(withId(R.id.consent_banner))
.check(matches(not(isDisplayed())));
// Verify SharedPreference
Context context = InstrumentationRegistry.getInstrumentation()
.getTargetContext();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
assertTrue(prefs.getBoolean("consent_given", false));
}
}
Key points
- Use
IdlingResourceif the banner is fetched asynchronously; register a custom IdlingResource that fires when the WebView finishes loading. - For WebView‑based banners, add
implementation 'androidx.test.espresso:espresso-web:3.5.1'and switch to the WebView context withonWebView().withElement(findElement(Locator.ID, "accept-button")).perform(webClick()).
UI Automator for cross‑app scenarios
When the consent flow launches a Chrome Custom Tab, UI Automator can interact with the Chrome UI:
@Test
public void customTabConsent_shownBeforeNavigation() {
UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
// Assume MainActivity has a button that opens the privacy page
onView(withId(R.id.open_privacy)).perform(click());
// Wait for Custom Tab to appear
UiObject2 tab = device.wait(Until.findObject(By.clazz("androidx.customview.widget.ViewPager")),
5000);
assertNotNull(tab);
// Check for consent banner inside the tab (WebView)
UiObject2 banner = device.findObject(By.text("We use cookies"));
assertTrue(banner.isDisplayed());
// Accept within the tab
UiObject2 acceptBtn = device.findObject(By.id("accept-button"));
acceptBtn.click();
// Verify that the target URL loads after consent
UiObject2 loaded = device.wait(Until.findObject(By.text("Privacy Policy")),
8000);
assertNotNull(loaded);
}
Appium for hybrid/WebView testing
Appium lets you drive the native container and then switch to the WebView context to manipulate the consent banner. The following Python snippet demonstrates a full flow on an emulator or real device.
from appium import webdriver
from appium.webdriver.common.mobileby import MobileBy
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
caps = {
"platformName": "Android",
"deviceName": "Pixel_4_API33",
"appPackage": "com.example.app",
"appActivity": ".MainActivity",
"automationName": "UiAutomator2",
"noReset": True
}
driver = webdriver.Remote("http://localhost:4723/wd/hub", caps)
wait = WebDriverWait(driver, 15)
# 1. Wait for native banner container
banner = wait.until(EC.presence_of_element_located((MobileBy.ID, "consent_banner")))
assert banner.is_displayed()
# 2. Switch to WebView context (assuming the banner lives inside a WebView)
webview_context = [ctx for ctx in driver.contexts if "WEBVIEW" in ctx][0]
driver.switch_to.context(webview_context)
# 3. Click the Accept button inside the WebView
accept_btn = wait.until(EC.element_to_be_clickable((MobileBy.ID, "accept-button")))
accept_btn.click()
# 4. Return to native context
driver.switch_to.context(driver.contexts[0])
# 5. Verify banner disappeared
native_banner = wait.until(EC.invisibility_of_element_located((MobileBy.ID, "consent_banner")))
assert not native_banner.is_displayed()
driver.quit()
Tips
- Set
chromeOptions: { androidProcess: com.example.app:webview }if you need to target a specific WebView process. - Enable
autoGrantPermissionsto avoid permission dialogs interfering with the test.
SUSATest autonomous exploration
SUSA can be invoked from the command line after installing the agent:
pip install susatest-agent
susatest-agent run \
--apk path/to/app-release.apk \
--personas curious impatient novice adversarial elderly accessibility power_user \
--output ./susatest-report.json \
--timeout 300
The agent explores the app without any pre‑written scripts. Each persona drives a distinct interaction style:
- Curious taps every visible element, scrolls aggressively, and opens overflow menus.
- Impatient performs rapid taps, often double‑tapping before animations finish, which can expose race conditions.
- Novice follows suggested UI flows (e.g., “Sign up”, “Login”) and pauses longer on dialogs.
- Adversarial attempts to dismiss dialogs by tapping outside, uses the back button aggressively, and tries to force‑close the app.
- Elderly simulates reduced dexterity with longer press durations and avoids small targets.
- Accessibility enables TalkBack, Switch Access, and large font sizes before exploring.
- Power user utilizes shortcuts, long‑press menus, and quick settings toggles.
During the run, SUSA records:
- Whether a consent banner ever appeared for each persona.
- If any interaction caused a crash, ANR, or unhandled exception.
- Network calls made before a consent decision (captured via built‑in VPN‑style traffic interception).
- Accessibility failures (missing contentDescriptions, contrast issues, focus order problems).
Because SUSA does not rely on deterministic locators, it can discover issues such as a banner that only appears after a specific sequence of navigation (e.g., opening the side drawer, then tapping “Settings”, then returning to the home screen) – a path that a scripted test would never consider unless explicitly added.
---
Edge Cases That Only Appear in Production
| Category | Situation | Why it hides in scripted tests | Detection tip |
|---|---|---|---|
| Locale‑specific text | Banner uses a hard‑coded English string for “Accept” in a French locale | Scripts often set locale once at test start and never change it mid‑run | Run a matrix of locales and verify string resources |
| Dark mode rendering | Banner background set to #FFFFFF causing low contrast in dark mode | Emulators may start in light mode; tests forget to toggle uiMode | Use adb shell cmd uimode night yes/no before launch |
| Orientation‑triggered reload | Banner fetches fresh HTML on each rotation due to missing android:configChanges | Automated tests frequently lock orientation to portrait | Rotate device programmatically and watch network logs |
| Low‑memory killer | System kills the WebView process while the banner is visible, leaving a blank screen | Most test devices have ample memory; CI runners rarely simulate pressure | Use adb shell am send-trim-memory com.example.app MODERATE |
| Network latency | Consent request takes >10 s; app shows a spinner that never dismisses because timeout logic is missing | Tests run on localhost or fast LAN, never throttling | Apply tc qdisc add dev eth0 root netem delay 8000ms |
| Consent revocation after update | New app version clears old SharedPreferences key, causing banner to reappear despite prior acceptance | Update scenarios are rarely exercised in CI | Simulate an upgrade: install v1, give consent, install v2 over it, launch |
| Multiple consent layers | First‑party banner + third‑party ad SDK banner both present; SDK ignores the first‑party decision | Scripts test only the app’s own banner | Integrate a mock ad SDK that logs when it initializes |
| Banner overlapping system UI | On devices with a notch or gesture bar, the banner appears under the cut‑out, making buttons untappable | Screenshots taken on emulator without notch | Test on a range of device profiles (Pixel 4 XL, Samsung Galaxy S22, etc.) |
| Accessibility override (font scale 200 %) | Layout uses fixed dp heights causing text cutoff | Many UI tests rely on default font size | Set settings put system font_scale 2.0 and rerun |
| Insecure SharedPreferences storage | Consent flag stored in MODE_WORLD_READABLE (deprecated but still present in some legacy code) | Automated checks rarely inspect file permissions | Use adb shell run-as com.example.app ls -l /data/data/com.example.app/shared_prefs/ |
| SameSite=None without Secure | Backend sends Set‑Cookie: sessionid=abc123; SameSite=None missing Secure flag | Cookie inspection is often omitted from UI tests | Capture Set‑Cookie headers and assert presence of Secure |
| XSS via cookie value | Malicious cookie contains that gets injected into a WebView via document.cookie | Tests rarely inject malicious values; they assume sane data | Use mitmproxy to rewrite Set‑Cookie with a script payload and verify it does not execute |
These edge conditions are precisely where autonomous, persona‑driven exploration shines: a curious user may repeatedly open the navigation drawer, an impatient user may spam the back button while a banner is loading, and an accessibility user with enlarged fonts may trigger layout overflows that a scripted test never reaches.
---
Accessibility Testing Deep Dive
TalkBack navigation order
- Open TalkBack (
Settings → Accessibility → TalkBack). - Launch the app.
- Swipe right repeatedly; the focus should move from the status bar (if visible) to the banner title, then to each action button, then to any underlying content.
- If focus jumps or skips an element, inspect the view’s
android:importantForAccessibilityattribute; set it toyesfor banner children.
Switch Access
- Connect a switch device or use the built‑in “Camera Switch” (
Settings → Accessibility → Switch Access). - Assign “Select” to a switch and observe whether you can highlight and activate the Accept and Reject buttons without needing precise touch.
Font scaling and layout breaks
- Go to
Settings → Accessibility → Font sizeand choose Largest (200 %). - Launch the app and verify that the banner’s height expands, text wraps, and buttons remain fully visible.
- If the banner gets clipped, consider using
ConstraintLayoutwithmatch_constraintsorScrollViewfor the banner container.
Color contrast verification
- Take a screenshot of the banner in both light and dark modes.
- Run a contrast checker (e.g., the WebAIM Contrast Checker) on the foreground/background colors extracted via
adb shell screencap -p /sdcard/banner.pngthen pull and inspect with an image editor. - Ensure a minimum ratio of 4.5:1 for normal text and 3:1 for large text.
Screen reader announcements for consent actions
- With TalkBack enabled, double‑tap the Accept button.
- The spoken feedback should indicate the outcome, e.g., “Accepted, cookies will be used”.
- If the app only announces “Button clicked”, add a
contentDescriptionthat changes based on the consent state or useandroid:hinton a hiddenTextViewthat TalkBack reads after the action.
---
Security and Privacy Checks
Inspecting cookie attributes via network logs
Using mitmproxy, add a script to print Set‑Cookie lines:
def response(flow):
for k, v in flow.response.headers.items_all():
if k.lower() == "set-cookie":
print(f"[SET-COOKIE] {v}")
Run the proxy, perform the consent flow, and verify each line contains Secure and either SameSite=Strict or SameSite=Lax.
Verifying SameSite and Secure flags
A quick Bash one‑liner to audit a mitmproxy dump file (dump.txt):
grep -i "set-cookie" dump.txt | while read line; do
if [[ ! "$line" =~ Secure ]] || [[ ! "$line" =~ SameSite=(Strict|Lax) ]]; then
echo "NON‑COMPLIANT: $line"
fi
done
Any output indicates a cookie that fails the baseline security policy.
Ensuring no sensitive data in cookie value
Extract cookie values and run a regex scan for patterns that look like email addresses, tokens, or personal identifiers:
grep -oP 'sessionid=\K[^;]+' dump.txt | grep -Ei '[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}'
If the command returns matches, the app is inadvertently storing PII in a cookie.
Testing consent revocation clears cookies
- Accept consent → capture cookie jar (
mitmproxy --set save_stream_file=cookies_before). - Revoke consent via settings → capture again (
cookies_after). - Diff the two files: any tracking cookie present in
aftersignals a failure.
Checking for third‑party tracking without consent
Configure mitmproxy to block all domains except those explicitly whitelisted (your own API and the CMP). Run the app; any request that gets blocked indicates a tracker that fired before consent.
---
Checklist for Cookie Consent Testing on Android
| ✅ Item | Description |
|---|---|
| Functional |
Test Your App Autonomously
Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.
Try SUSA Free