This skill should be used when the user asks for "test review", "test coverage", "improve F# tests", "review tests", "test quality", "testing audit", or wants analysis of F# test suites for coverage gaps, edge cases, and testing best practices.
Install
npx skillscat add bromanko/llm-agents/fsharp-test-review Install via the SkillsCat registry.
F# Test Review
Action required: Run /review fsharp test to start an interactive test review. Do not perform the review manually.
Analyze F# test code for coverage gaps, edge case handling, test quality, and testing best practices.
Scope Determination
First, determine what to review:
- If the user specifies test files: Review those paths
- If the user specifies source files: Find corresponding tests and review coverage
- If no scope specified: Review working changes
- Check for
.jjdirectory first, usejj diffif present - Otherwise use
git diffto identify changed files - Look at both changed source and test files
- Check for
Test files are typically in a separate test project (e.g., MyProject.Tests/) with Tests.fs suffix or in files containing [<Tests>] / [<Fact>] / [<Test>] attributes.
Review Process
- Map source to tests: Identify which source modules have test coverage
- Analyze test coverage: Check if key functionality is tested
- Review test quality using checklist below
- Identify missing edge cases
- Output findings in the standard format
Test Review Checklist
Coverage Gaps
- Public functions have corresponding tests
- Error paths tested (
Result.Errorcases, exception handling) - Edge cases covered:
- Empty collections (
[],Map.empty,Array.empty) - Zero/negative numbers where applicable
- Empty strings, null strings (for interop boundaries)
- Boundary conditions
Nonecases for Option types
- Empty collections (
- Integration points tested (module interactions)
- Discriminated union — all cases exercised in tests
Test Quality
- Tests are focused (one concept per test)
- Test names describe behavior, not implementation
- Assertions are specific (not just "returns Ok")
- Tests are deterministic (no flaky tests)
- Setup/teardown handled appropriately
- Tests run independently (no order dependencies)
- Arrange-Act-Assert pattern followed
Skipped & Disabled Tests
- Flag any use of skip markers:
[<Fact(Skip="...")>](xUnit),[<Ignore("...")>](NUnit),ptest/ptestCase/ptestList(Expecto) — especially if recently added - Look for tests with trivially passing assertions replacing what should be real checks (a sign the test was gutted to pass)
- Check for commented-out tests or test bodies that have been emptied
- Watch for
TODO/FIXMEcomments suggesting the test was too hard to fix and was bypassed instead - These patterns are HIGH severity when they appear to be workarounds (e.g., an LLM disabling a test it couldn't fix rather than addressing the underlying failure)
Test Organization
- Test projects mirror source structure
- Related tests grouped logically (module or
testListgrouping) - Helper functions extracted for common setup
- Test data defined clearly (test fixtures or inline)
- No duplicate test logic
testList/testCasehierarchy clear (Expecto) or class grouping clear (xUnit/NUnit)
Assertion Patterns (Framework-Specific)
Expecto
Expect.equalfor exact matchesExpect.isTrue/Expect.isFalsefor booleansExpect.isOk/Expect.isErrorfor ResultsExpect.throwsfor expected exceptionsExpect.containsAllfor collection membership
xUnit
Assert.Equalfor exact matchesAssert.True/Assert.Falsefor booleans- Custom assertions for domain types
Assert.Throws<T>for expected exceptions
FsUnit
should equalfor exact matchesshould be ofCasefor DU case matchingshould throw typeof<T>for expected exceptions
Edge Case Coverage
For each function, consider:
- What happens with empty input?
- What happens at boundaries (0,
Int32.MaxValue, etc.)? - What happens with invalid input?
- What happens with very large input?
- Are all discriminated union cases exercised?
- Are computation expression edge cases tested (early return, exceptions)?
Property-Based Testing (FsCheck)
Identify functions that would benefit from property tests:
- Pure functions with clear invariants
- Serialization/deserialization (roundtrip property)
- Mathematical operations (commutativity, associativity)
- Collection operations (length preservation, etc.)
- Parsers (valid input always parses)
- Custom generators for domain types
Suggest using FsCheck integration with the project's test framework.
Test Isolation
- External services mocked/stubbed (use interfaces or function parameters)
- File system interactions isolated
- Time-dependent tests use controlled time (
DateTimeOffsetinjection) - Random values seeded for reproducibility
- No shared mutable state between tests
- Database tests use transactions or test containers
Error Scenario Testing
- Invalid input handled gracefully
- External failures handled (network, file, etc.)
- Concurrent access scenarios (if applicable)
- Resource exhaustion considered
- Timeout behavior tested
- Cancellation token behavior tested for async operations
Output Format
Present findings as:
## Findings
### [SEVERITY] Issue Title
**File:** `path/to/FileTests.fs:LINE` (or source file if missing tests)
**Category:** testing
**Issue:** Description of the testing gap or quality issue.
**Suggestion:** What to test or how to improve, with example if helpful.
**Effort:** trivial|small|medium|large
---Use severity indicators:
- HIGH: Critical untested paths, missing error handling tests
- MEDIUM: Missing edge cases, unclear test intent
- LOW: Test organization, minor improvements
Summary
After all findings, provide:
- Total count by severity
- Coverage summary (modules with/without tests)
- Top testing priorities
- Property testing candidates
- Overall test suite assessment (1-2 sentences)