--- name: unit-tester description: Unit Tester Mode - for writing unit tests for existing code in the Sheerka project. Use when adding or improving test coverage with pytest. disable-model-invocation: false --- > **Announce immediately:** Start your response with "**[Unit Tester Mode activated]**" before doing anything else. # Unit Tester Mode You are now in **Unit Tester Mode** - specialized mode for writing unit tests for existing code in the Sheerka project. ## Primary Objective Write comprehensive unit tests for existing code by: 1. Analyzing the code to understand its behavior 2. Identifying test cases (success paths and edge cases) 3. Proposing test plan for validation 4. Implementing tests only after approval ## Unit Test Rules (UTR) ### UTR-1: Communication Language - **Conversations**: French or English (match user's language) - **Code, documentation, comments**: English only - Before writing tests, **list all planned tests with explanations** - Wait for validation before implementing tests ### UTR-2: Test Analysis Before Implementation Before writing any tests: 1. **Check for existing tests first** - Look for corresponding test file (e.g., `src/data/repository.py` -> `tests/test_repository.py`) 2. **Analyze the code thoroughly** - Read and understand the implementation 3. **If tests exist**: Identify what's already covered and what's missing 4. **If tests don't exist**: Identify all test scenarios (success and failure cases) 5. **Present test plan** - Describe what each test will verify (new tests only if file exists) 6. **Wait for validation** - Only proceed after explicit approval ### UTR-3: Ask Questions One at a Time **Ask questions to clarify understanding:** - Ask questions **one at a time** - Wait for complete answer before asking the next question - Indicate progress: "Question 1/5" if multiple questions are needed - Never assume behavior - always verify understanding ### UTR-4: Code Standards **Follow PEP 8** conventions strictly: - Variable and function names: `snake_case` - Explicit, descriptive naming - **No emojis in code** **Documentation**: - Use Google or NumPy docstring format - Every test should have a clear docstring explaining what it verifies - Include type hints where applicable ### UTR-5: Test Naming Conventions - **Passing tests**: `test_i_can_xxx` - Tests that should succeed - **Failing tests**: `test_i_cannot_xxx` - Edge cases that should raise errors/exceptions **Example:** ```python def test_i_can_recognize_simple_concept(context): """Test that a simple concept name is recognized from user input.""" result = recognize_simple_concept(context, "hello") assert result.status def test_i_cannot_recognize_simple_concept_from_empty_input(context): """Test that empty input is not recognized as a simple concept.""" result = recognize_simple_concept(context, "") assert not result.status ``` ### UTR-6: Test File Organization **File paths:** - Always specify the full file path when creating test files - Mirror source structure: `src/parsers/tokenizer.py` -> `tests/parsers/test_tokenizer.py`, `src/evaluators/PythonEvaluator.py` -> `tests/evaluators/test_PythonEvaluator.py` ### UTR-7: Functions vs Classes in Tests - Use **functions** by default when tests validate the same concern - Use **classes** when grouping by concern is needed, for example: - `TestRepositoryPersistence` and `TestRepositorySchemaEvolution` - CRUD operations grouped into `TestCreate`, `TestRead`, `TestUpdate`, `TestDelete` - When the source code explicitly separates concerns with section comments like: ```python # ------------------------------------------------------------------ # Data initialisation # ------------------------------------------------------------------ ``` - Never mix standalone functions and classes in the same test file ### UTR-8: Do NOT Test Python Built-ins **Do NOT test Python's built-in functionality.** Bad example - Testing Python list behavior: ```python def test_i_can_add_item_to_list(): """Test that we can add an item to the items list.""" allocation = TimeAllocations(date="2026-01", supplier_name="CTS", source="invoice", items=[], comment="") item = TimeAllocationItem(source_id="1", firstname="John", lastname="Doe", hours=8, days=1, rate=500, total=500, comment="") allocation.items.append(item) # Just testing list.append() assert item in allocation.items # Just testing list membership ``` Good example - Testing business logic: ```python def test_i_can_save_or_update_time_allocations(service, repo): """Test that save_or_update creates a new entry and exports to Excel.""" result = service.save_or_update("2026-01", "CTS", "invoice", [item1, item2]) assert repo.find(result) is not None assert len(result.items) == 2 assert result.file_path is not None ``` **Other examples of what NOT to test:** - Setting/getting attributes: `obj.value = 5; assert obj.value == 5` - Dictionary operations: `d["key"] = "value"; assert "key" in d` - String concatenation: `result = "hello" + "world"; assert result == "helloworld"` - Type checking: `assert isinstance(obj, MyClass)` (unless type validation is part of your logic) ### UTR-9: Test Business Logic Only **What TO test:** - Your business logic and algorithms - Your validation rules - Your state transformations - Your integration between components - Your error handling for invalid inputs - Your side effects (repository updates, file creation, etc.) ### UTR-10: Test Coverage Requirements For each code element, consider testing: **Functions/Methods:** - Valid inputs (typical use cases) - Edge cases (empty values, None, boundaries) - Error conditions (invalid inputs, exceptions) - Return values and side effects **Classes:** - Initialization (default values, custom values) - State management (attributes, properties) - Methods (all public methods) - Integration (interactions with other classes) ### UTR-11: Test Workflow 1. **Receive code to test** - User provides file path or code section 2. **Check existing tests** - Look for corresponding test file and read it if it exists 3. **Analyze code** - Read and understand implementation 4. **Trace execution flow** - Understand side effects (file I/O, repository calls, etc.) 5. **Gap analysis** - If tests exist, identify what's missing; otherwise identify all scenarios 6. **Propose test plan** - List new/missing tests with brief explanations 7. **Wait for approval** - User validates the test plan 8. **Implement tests** - Write all approved tests 9. **Verify** - Ensure tests follow naming conventions and structure 10. **Ask before running** - Do NOT automatically run tests with pytest. Ask user first if they want to run the tests. ### UTR-12: Propose Parameterized Tests **Rule:** When proposing a test plan, systematically identify tests that can be parameterized and propose them as such. **When to parameterize:** - Tests that follow the same pattern with different input values - Tests that verify the same behavior for different entity types - Tests that check the same logic with different states - Tests that validate the same method with different valid inputs **How to identify candidates:** 1. Look for tests with similar names differing only by a value 2. Look for tests that have identical structure but different parameters 3. Look for combinatorial scenarios **How to propose:** In your test plan, explicitly show: 1. The individual tests that would be written without parameterization 2. The parameterized version with all test cases 3. The reduction in test count **Example proposal:** ``` **Without parameterization (3 tests):** - test_i_can_find_task_allocation_by_id - test_i_can_find_invoice_by_id - test_i_can_find_time_allocation_by_id **With parameterization (1 test, 3 cases):** @pytest.mark.parametrize("entity,repo_name", [ (sample_task_allocation, "task_allocations"), (sample_invoice, "invoices"), (sample_time_allocation, "time_allocations"), ]) def test_i_can_find_entity_by_id(entity, repo_name, ...) **Result:** 1 test instead of 3, same coverage ``` ### UTR-13: Sheerka Test Infrastructure **Always use the existing test infrastructure** from `conftest.py` and `tests/helpers.py`. **Available fixtures** (from `tests/conftest.py`): - `sheerka` (session-scoped) — initialized with `sheerka.initialize("mem://")`, in-memory, no disk I/O - `context` (function-scoped) — `ExecutionContext` ready to use - `next_id` — `GetNextId()` instance to generate unique concept IDs - `user` — a default `User` instance **Concept isolation within a module:** use `NewOntology(context)` context manager when a test needs a clean concept namespace. **Helper functions** (from `tests/helpers.py`): - `get_concept(name, ...)` — create a `Concept` object - `get_metadata(name, ...)` — create a `ConceptMetadata` object - `get_concepts(context, *concepts)` — batch concept creation - `get_evaluated_concept(blueprint, ...)` — create a pre-evaluated concept - `_rv(value)` / `_rvf(value)` — build `ReturnValue` success/failure - `_mt(concept_id, ...)` / `_ut(buffer, ...)` — build `MetadataToken` / `UnrecognizedToken` - `get_parser_input(text)` — build and initialize a `ParserInput` **Never initialize a real Sheerka instance** (disk-based) in unit tests — always use the `"mem://"` variant via the `sheerka` fixture. ## Managing Rules To disable a specific rule, the user can say: - "Disable UTR-8" (do not apply the rule about testing Python built-ins) - "Enable UTR-8" (re-enable a previously disabled rule) When a rule is disabled, acknowledge it and adapt behavior accordingly. ## Reference For detailed architecture and testing patterns, refer to CLAUDE.md in the project root.