Back to Bitchat Android

Testing conventions

docs/testing-conventions.md

2.0.22.9 KB
Original Source

Testing conventions

Purpose

These conventions keep the client-rewrite suite deterministic, reproducible, and portable across implementations.

Test locations

Test typeLocationNaming
JVM unit and contract testsapp/src/test/*Test.kt
Shared deterministic fakes and fixturesapp/src/test/**/testsupport/Descriptive fixture name
Robolectric testsapp/src/test/*RobolectricTest.kt
Android instrumented testsapp/src/androidTest/*InstrumentedTest.kt
Coverage-tool teststools/coverage/test_*.py
Interoperability fixturesapp/src/test/resources/contracts/Protocol and version in filename

Required behavior

  • Tests must not use arbitrary sleeps. Advance a fake clock or coroutine test scheduler instead.
  • Tests must not require public relays, internet access, Bluetooth hardware, or a user's persisted data.
  • Time, randomness, dispatchers, storage, and transports must be injectable in code exercised by state-machine tests.
  • Randomized failures must print a reproduction seed. Use TEST_SEED for a specific replay.
  • Mutable byte arrays returned by fixtures and fakes must be defensively copied.
  • Negative security tests must assert fail-closed behavior.
  • Protocol round trips must be paired with literal golden vectors for critical externally visible formats.
  • Asynchronous tests must have a deterministic completion condition and a bounded timeout.
  • A fixed bug must retain its smallest reproducing input as a regression test.

Naming

Test names should describe observable behavior:

kotlin
@Test
fun `replayed ciphertext is rejected without advancing receive state`() {
    // ...
}

Avoid names tied to private methods or temporary implementation structure.

Fixtures and seeds

Reusable Kotlin fixtures live under com.bitchat.android.testsupport. ReproducibleTestSeed resolves TEST_SEED and provides a reproduction hint:

sh
TEST_SEED=12345 ./gradlew clientRewriteContractTest

Never use production keys, contact information, messages, or other user data in fixtures.

Coverage

Run the full report and non-regression floor:

sh
./gradlew clientRewriteContractTest

Reports are written to:

  • app/build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml
  • app/build/reports/jacoco/jacocoTestReport/html/

Check executable production lines changed from the base branch:

sh
COVERAGE_BASE_REF=origin/main ./gradlew checkChangedLineCoverage

Generated resource classes, Compose-generated singleton classes, platform bridges, and vendored Noise code are excluded from first-party coverage metrics.

Quarantine and skips

  • A flaky test must be fixed, not silently retried.
  • A temporary quarantine must include an issue and removal condition.
  • Unexpected skips fail review. Existing skips must be restored or replaced by equivalent coverage.