Developer Wiki / Test adapter

Test without a device

Use a fake capture port to verify inventory and receiving rules in CI before a physical handheld is available.

Start the stepsBack to Wiki

By the end

Business logic tests that do not require an Android device, scanner or vendor binary.

For Teams that want fast unit tests and a clean boundary between workflow rules and hardware calls.

Before you start

Keep the first test small and repeatable.

ModuleTest adapter
LevelBeginner
AudienceTeams that want fast unit tests and a clean boundary between workflow rules and hardware calls.
  • A domain function that consumes a normalized capture event.
  • A test framework already running in the project.
  • A clear rule for what should happen with duplicate, empty and invalid values.

Step by step

Build it in 4 deliberate passes.

Each pass produces a checkable result. Keep device-specific calls in the adapter and keep the workflow portable.

  1. 01

    Depend on your port, not the device class

    The receiving workflow should know about CaptureEvent and CapturePort. It should not import a scanner manager, broadcast constant or model-specific type.

  2. 02

    Emit deterministic events

    Give the fake adapter explicit methods for a valid scan, duplicate scan, empty value and failure. Tests become readable when the input describes the operator scenario.

  3. 03

    Assert business outcomes

    Check stock movement, validation messages and duplicate handling. Do not assert that a vendor callback was called from a business test; that belongs in the hardware adapter test.

  4. 04

    Keep the hardware test small

    When a device is available, run a focused smoke test for opening, triggering, decoding and closing. The larger workflow suite should remain fast and device-free.

Copyable pattern / Kotlin

Start with the boundary, then bind the device.

Original test double. It is safe to publish because it does not include a vendor class or binary.

fake-capture-port-test.kt

class FakeCapturePort : CapturePort {
  private var listener: ((CaptureEvent) -> Unit)? = null

  override fun open() = Unit
  override fun start() = Unit
  override fun stop() = Unit
  override fun close() = Unit

  override fun setListener(listener: (CaptureEvent) -> Unit) {
    this.listener = listener
  }

  fun emit(value: String, symbology: String? = "test") {
    listener?.invoke(CaptureEvent(value, symbology, Instant.now()))
  }
}

@Test
fun receiving_a_valid_barcode_updates_stock() {
  val scanner = FakeCapturePort()
  val workflow = ReceivingWorkflow(scanner)

  workflow.start()
  scanner.emit("SKU-001")

  assertEquals(ReceivingState.Accepted("SKU-001"), workflow.state)
}
Original pattern. No vendor binary is included on this page.

When the happy path breaks

Make recovery part of the first implementation.

Operators experience the failure state, not the API call. Translate device signals into a useful next action.

Test needs a real device to run

Move hardware setup into a small adapter smoke test and keep domain rules on the fake port.

The same scan is accepted twice

Add an explicit duplicate event and assert the state transition once.

An empty value reaches the repository

Reject at the normalization boundary and expose a user-recoverable state.

Before you ship

Use this checklist on the target configuration.

  1. 01

    Run the fake adapter tests on every CI build.

  2. 02

    Cover valid, empty, duplicate, oversized and transport-failure cases.

  3. 03

    Keep the device-specific adapter behind one package boundary.

  4. 04

    Run one physical-device smoke test separately for every approved baseline.

Next step: Connect the approved scanner SDK to CapturePort without changing the receiving workflow.

Further reading

Use platform guidance for the parts the device SDK does not own.

These references cover Android lifecycle, broadcast, testing and architecture patterns. Device-specific compatibility still needs a model-level validation record.

Need the exact device binding?

Bring the model, workflow and app build. We will scope the validation.

Validate my application