TestKase
TestKase
|Docs
AutomateMobile

Marking tests

Report each test as passed or failed, and split one session into as many tests as you like.

Appium has no idea what a "test" is. To it, driver is just a connection to a phone.

So unless you tell us, a session has no result. It also counts as one test, however much it did.

Two lines fix both. This page is those two lines.

1. Saying whether a test passed or failed

One line, at the end of your test. It is the same line everywhere:

LanguageThe line
Java((JavascriptExecutor) driver).executeScript("testkase-status=passed");
JavaScriptawait driver.executeScript("testkase-status=passed", []);
Pythondriver.execute_script("testkase-status=passed")

In Java, AppiumDriver already implements JavascriptExecutor — nothing extra to import.

The session then shows green in your dashboard. Send failed instead and it shows red.

Only two words work. passed or failed. Anything else — PASSED, success, ok — is thrown away, and nothing warns you.

Report the failure where a crash cannot skip it. If your test blows up halfway, every line after the blow-up is skipped, including the one that reports the result:

JavascriptExecutor js = (JavascriptExecutor) driver;   // AppiumDriver implements it
try {
    driver.findElement(AppiumBy.accessibilityId("login-submit")).click();

    js.executeScript("testkase-status=passed");
} catch (Throwable t) {
    js.executeScript("testkase-status=failed");
    throw t;
}
try {
  await driver.$("~login-submit").click();

  await driver.executeScript("testkase-status=passed", []);
} catch (err) {
  await driver.executeScript("testkase-status=failed", []);
  throw err;
}
try:
    driver.find_element(AppiumBy.ACCESSIBILITY_ID, "login-submit").click()

    driver.execute_script("testkase-status=passed")
except Exception:
    driver.execute_script("testkase-status=failed")
    raise

2. Splitting a session into several tests

Every session already counts as one test, named from the name in your capabilities. If you start a fresh driver per test, you are done.

On mobile, though, tests usually share one driver: starting one installs and launches the app, which costs real seconds every time.

If that is you, name each test and they become separate rows, each with its own video clip and its own logs.

Mark where a test starts with testkase-name=, and where it ends with the status line you already have. The first testkase-name= renames the test we opened for you; each one after that closes the previous test and starts a new one.

@Test
public void signInWithValidCredentials() {
    JavascriptExecutor js = (JavascriptExecutor) driver;
    js.executeScript("testkase-name=Sign in with valid credentials");   // this test starts
    try {
        driver.findElement(AppiumBy.accessibilityId("login-submit")).click();

        js.executeScript("testkase-status=passed");                     // it ends, green
    } catch (Throwable t) {
        js.executeScript("testkase-status=failed");                     // it ends, red
        throw t;
    }
}
it("signs in with valid credentials", async () => {
  await driver.executeScript("testkase-name=Sign in with valid credentials", []);  // starts
  try {
    await driver.$("~login-submit").click();

    await driver.executeScript("testkase-status=passed", []);                      // green
  } catch (err) {
    await driver.executeScript("testkase-status=failed", []);                      // red
    throw err;
  }
});
def test_sign_in_with_valid_credentials(driver):
    driver.execute_script("testkase-name=Sign in with valid credentials")   # this test starts
    try:
        driver.find_element(AppiumBy.ACCESSIBILITY_ID, "login-submit").click()

        driver.execute_script("testkase-status=passed")                     # it ends, green
    except Exception:
        driver.execute_script("testkase-status=failed")                     # it ends, red
        raise

Put them in your hooks, not in every test

Writing them by hand in each test is how one gets forgotten:

// TestNG
@BeforeMethod
public void startTest(Method method) {
    ((JavascriptExecutor) driver).executeScript("testkase-name=" + method.getName());
}

@AfterMethod
public void endTest(ITestResult result) {
    String status = result.isSuccess() ? "passed" : "failed";
    ((JavascriptExecutor) driver).executeScript("testkase-status=" + status);
}
// Mocha
beforeEach(async function () {
  await driver.executeScript(`testkase-name=${this.currentTest.title}`, []);
});

afterEach(async function () {
  const status = this.currentTest.state === "passed" ? "passed" : "failed";
  await driver.executeScript(`testkase-status=${status}`, []);
});
# pytest
@pytest.fixture(autouse=True)
def mark_test(driver, request):
    driver.execute_script(f"testkase-name={request.node.name}")
    yield
    status = "failed" if request.node.rep_call.failed else "passed"
    driver.execute_script(f"testkase-status={status}")

Close each test before the next one starts. If you forget, the next testkase-name= closes the one before it as failed — we cannot tell whether it worked. Forget it on the last test of a session and that test keeps showing as still running.