Assertions
Mobile assertions are web-first: they read the screen again until the condition holds or the timeout (mobile.timeouts.actionMs, default 5000 ms) runs out. A screen that is still loading or animating does not fail the test, and you need no sleeps.
import static io.testfly.mobile.MobileAssertions.expect; // already available in BaseMobileTest, MobilePage and MobileSupport
expect(screen().getByText("Welcome back")).toBeVisible();
expect(screen().getByTestId("balance")).toHaveText(Pattern.compile("^\\d+,\\d{2} ₺$"));
expect(screen().getByRole(MobileRole.LISTITEM)).toHaveCount(3);
A failed assertion throws MobileAssertionError with the last reason and the elements seen:
expect(getByTestId("balance")).toHaveText(/^\d+,\d{2} ₺$/) failed after 5000 ms: text differs: android.widget.TextView id="balance" text="--" [40,300 600x80]
Matchers
| Matcher | Passes when |
|---|---|
toBeVisible() | exactly one match is visible |
toBeHidden() | no match is visible (absent counts as hidden) |
toBeEnabled(), toBeDisabled() | the one match is enabled / disabled |
toBeChecked() | the one match is checked (checkbox, radio, switch) |
toBeSelected() | the one match is selected (tab, segment, list row) |
toBeFocused() | the one match has the input focus |
toHaveText("Welcome") | the displayed text (text, else label, else value) equals the string |
toHaveText("welcome", true) | the same, ignoring case and normalizing whitespace (runs of spaces, leading/trailing blanks) |
toHaveText(Pattern) | the displayed text matches the regular expression (find()) |
toContainText("back") | the displayed text contains the string, case-insensitive |
toHaveValue("[email protected]") | the value equals the string (text fields, sliders) |
toBeEmpty() | the element has no text and no value; an iOS field that only shows its placeholder counts as empty |
toHaveCount(n) | the query matches exactly n elements |
Single-element matchers apply strict mode; toBeHidden() and toHaveCount(n) look at every match.
Negation, timeout and message
expect(screen().getByText("Loading")).not().toBeVisible(); // same as toBeHidden()
expect(screen().getByTestId("username")).not().toBeEmpty();
expect(screen().getByText("Report ready")).withTimeout(Duration.ofSeconds(30)).toBeVisible();
expect(screen().getByTestId("total"), "cart total after the coupon").toHaveText("90,00 ₺");
not()negates the next matcher.not().toBeVisible()passes when nothing visible matches.withTimeout(Duration)changes the retry budget of this assertion only. A locator's ownwithTimeoutis used by default.expect(locator, message)putsmessageon the first line of a failure.
In page objects, keep assertions in intent methods (shouldShowBalance()), so tests stay readable.
Retrying any value: expectPoll
expectPoll(supplier) retries any Java value, for checks that are not about one element: device state, a count from several locators, a backend call.
import static io.testfly.mobile.MobileAssertions.expectPoll;
device().setOrientation(MobileOrientation.LANDSCAPE);
expectPoll(device()::orientation).toBe(MobileOrientation.LANDSCAPE);
expectPoll(() -> screen().getByRole(MobileRole.LISTITEM).count())
.withTimeout(Duration.ofSeconds(10))
.matches(n -> n >= 20, "at least 20 rows after scrolling");
toBe(expected)compares withequals;matches(predicate, description)takes any condition.- The default timeout is 5 seconds;
withTimeout(Duration)changes it. The supplier is called every 200 ms. - An exception from the supplier counts as a failed attempt; the failure message shows the last value or the last error.
Soft assertions
A soft assertion records a failure and lets the test continue, so one run reports every wrong field of a screen. Create a collector with MobileAssertions.softly(), assert through it, and call assertAll() at the end:
MobileSoftAssertions soft = MobileAssertions.softly();
soft.expect(profile.name).toHaveText("Ada Lovelace");
soft.expect(profile.email).toHaveText("[email protected]");
soft.expect(profile.badge, "premium badge").toBeVisible();
soft.assertAll();
assertAll()throws oneMobileAssertionErrorthat lists every failure (each also attached as a suppressed exception), then clears the list. With no failures it does nothing.failures()returns the failures collected so far.- A collector is not thread-safe: create one per test.
- Forgetting
assertAll()hides the failures; call it in the test, not in a page object.
The softAssert() of the base classes is TestFly's web/general soft assertion API; use MobileAssertions.softly() for mobile locators.