--- icon: lucide/test-tube tags: - Testing --- # Testing without a model You want to assert what a pipeline produces without downloading an NER model or reaching the network. `ExactMatchDetector` gives you that. You tell it which literal values map to which label, and it finds their occurrences with a plain regex. The rest of the pipeline runs unchanged. A test therefore exercises real linking, resolution, and de-identification against a detector whose output you control. Use this to test a pipeline you assembled, or a custom component you wrote, against `<>`{ .placeholder } rather than a model's guess. ## Assert one de-identified string Build a pipeline with `ExactMatchDetector`, run it on a text, and compare `result.text` to the expected output. ```python import asyncio from piighost.components.detector import ExactMatchDetector from piighost.pipeline import AnonymizationPipeline detector = ExactMatchDetector({"John Doe": "PERSON", "Paris": "LOCATION"}) pipeline = AnonymizationPipeline(detector) async def main() -> None: result = await pipeline.anonymize("John Doe lives in Paris.") assert result.text == "<> lives in <>." asyncio.run(main()) ``` `ExactMatchDetector` takes a mapping of literal value to label. It emits one detection per occurrence with confidence `1.0`, so its output never varies between runs. ## Write it as a pytest test The project runs pytest with `asyncio_mode = "auto"`, so an `async def test_...` needs no decorator. Assert both the exact output and the absence of the raw value. ```python from piighost.components.detector import ExactMatchDetector from piighost.pipeline import AnonymizationPipeline def build_pipeline(values: dict[str, str]) -> AnonymizationPipeline: return AnonymizationPipeline(ExactMatchDetector(values)) async def test_person_is_tokenized() -> None: """A detected person becomes its token, and the raw value is gone.""" pipeline = build_pipeline({"Alice": "PERSON"}) result = await pipeline.anonymize("Alice lives in Lyon.") assert result.text == "<> lives in Lyon." assert "Alice" not in result.text ``` If your own project runs pytest with the default synchronous mode, install `pytest-asyncio` and mark the test with `@pytest.mark.asyncio`, or set `asyncio_mode = "auto"` in your pytest config to drop the decorator. ## Assert that repeats share one token Entity linking groups every occurrence of a value under one entity, so a repeated name reuses its first token. `ExactMatchDetector` finds each occurrence, `ExactEntityLinker` groups them, and the assertion checks the shared `<>`{ .placeholder }. ```python async def test_repeat_shares_one_token() -> None: """A repeated value reuses its first token.""" pipeline = build_pipeline({"Alice": "PERSON"}) result = await pipeline.anonymize("Alice met Alice again.") assert result.text == "<> met <> again." ``` ## Test a custom component Every pipeline stage is a port, that is an interface any component can implement. So you can drop your own component in beside `ExactMatchDetector`, and let the deterministic detector feed it. Give the stage a fixed input through `ExactMatchDetector`, then assert on `result.text`. See [Extending piighost](../extending.md) for the ports and worked component examples.