264 lines
9.6 KiB
Markdown
264 lines
9.6 KiB
Markdown
---
|
|
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.
|