#1109·doctest

Feature Request: Official Android JNI + JUnit Integration Example

Author: evgeniy-glebaCreated May 7, 2026Updated May 10, 2026

Description

Summary

GTest has a well-documented, production-hardened Android integration path via Chromium's NativeTest harness — JNI bridge, Activity-based process entry, JUnit runner, test enumeration, result reporting. doctest has no equivalent. This makes it unnecessarily hard for C++ game engines, NDK libraries, and native Android projects to adopt doctest for on-device testing.

Request: An official example (or extras/ module) demonstrating a complete doctest → Android JUnit integration.

Motivation — What GTest Already Has

Chromium ships a mature harness for running GTest inside Android APKs (docs):

am instrument → NativeTestInstrumentationTestRunner
  → NativeUnitTestActivity (process entry)
    → NativeTest.java (JNI bridge, arg formatting)
      → testing::android::RunTests (native launcher)
        → RUN_ALL_TESTS()
      ← exit code
    ← JNI return
  ← JUnit assertion

There's also the simpler AndroidGTestRunner demo showing the minimal pattern. Both approaches let you:

  1. Enumerate tests via --gtest_list_tests and map them to JUnit test cases
  2. Run individual tests by passing --gtest_filter=Suite.Test through JNI
  3. Report results back to JUnit with per-test pass/fail granularity
  4. Integrate with CI (TeamCity, Jenkins) via standard JUnit XML output

What doctest Needs

doctest already has the right primitives but no Android wiring:

Capability doctest API Missing Piece
List all tests --list-test-cases flag JNI method to invoke it, parse output into List<String>
Run filtered tests --test-case= / -tc flag JNI method accepting String[] args
Get exit code context.run() returns int JNI method returning jint
Per-test callbacks context.setAsDefaultForAssertsOutOfTestCases() + reporters Custom reporter that calls back to Java via JNI on each test start/end
JUnit export XML (to-host) Not built-in Generate from Java-gradle side using test results

Proposed Scope

1. C++ side — doctest_android_runner.h

A helper header (or example file) providing:

cpp
// JNI-callable: returns JSON array of test case names
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_DoctestRunner_nativeListTests(JNIEnv*, jclass);

// JNI-callable: runs tests matching filter, returns exit code
extern "C" JNIEXPORT jint JNICALL
Java_com_example_DoctestRunner_nativeRunTests(JNIEnv*, jclass,
                                               jobjectArray args);

// JNI-callable: runs tests with a callback-based reporter
// that invokes Java methods on each test start/stop/assert
extern "C" JNIEXPORT jint JNICALL
Java_com_example_DoctestRunner_nativeRunTestsWithReporter(
    JNIEnv*, jclass, jobjectArray args, jobject javaReporter);

2. Java side — DoctestRunner.java

A JUnit-compatible runner that:

  • Calls nativeListTests() at discovery time
  • Creates one JUnit Description per doctest test case
  • Calls nativeRunTests(args) at execution time
  • Reports results as standard JUnit pass/fail events

3. Activity harness — DoctestActivity.java

A minimal Activity that:

  • Loads the native library
  • Receives test arguments via Intent extras
  • Calls the native runner on a background thread
  • Calls setResult() + finish() with the exit code
  • Does NOT call System.exit() — lets the Activity lifecycle handle teardown

4. Instrumentation test — DoctestTest.java

A JUnit test using ActivityScenario:

java
@RunWith(AndroidJUnit4.class)
public class DoctestTest {
    @Test
    public void runAllTests() throws Exception {
        Intent intent = new Intent(context, DoctestActivity.class);
        intent.putExtra("doctest_args", new String[]{"--teamcity"});

        try (ActivityScenario<DoctestActivity> scenario =
                 ActivityScenario.launchActivityForResult(intent)) {
            assertEquals(Activity.RESULT_OK,
                         scenario.getResult().getResultCode());
        }
    }
}

5. TeamCity / CI integration

  • Java side reads logcat or stdout, forwards to ##teamcity[...] format
  • Alternatively: parse doctest exit code + log, generate JUnit XML report

Prior Art

Why This Matters

C++ game engines and NDK libraries are increasingly using doctest for its compile-time speed and single-header simplicity. But Android testing tooling (Android Studio test runner, CI pipelines, TeamCity) all expect JUnit as the integration point. Without a bridge, teams either:

  • Skip on-device testing entirely
  • Write fragile ad-hoc JNI wrappers
  • Stick with GTest despite wanting to switch

A canonical example lowers the barrier dramatically.