Feature Request: Official Android JNI + JUnit Integration Example
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 assertionThere's also the simpler AndroidGTestRunner demo showing the minimal pattern. Both approaches let you:
- Enumerate tests via
--gtest_list_testsand map them to JUnit test cases - Run individual tests by passing
--gtest_filter=Suite.Testthrough JNI - Report results back to JUnit with per-test pass/fail granularity
- 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:
// 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
Descriptionper 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:
@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
- Chromium NativeTest harness — GTest reference implementation
- AndroidGTestRunner — minimal GTest+JUnit demo
- doctest
--list-test-cases— already exists - doctest custom reporters — IReporter interface for callbacks
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.
Source: doctest/doctest