Files
dotnet__skills/tests/dotnet-experimental/exp-test-maintainability/eval.yaml
Amaury Levé 93dc33da86 Stabilize and unify some test skills (#501)
* Deduplicate test skill references and clarify skill boundaries

- Move platform-detection.md and filter-syntax.md to plugins/dotnet-test/shared/,
  removing 3 identical copies of each from run-tests, mtp-hot-reload, and
  migrate-vstest-to-mtp reference directories.

- Move dotnet.md from exp-test-smell-detection/extensions/ to shared/ as
  dotnet-test-frameworks.md in both dotnet-test and dotnet-experimental plugins.
  Update exp-assertion-quality, exp-test-boilerplate-detection, exp-test-tagging,
  and test-anti-patterns to reference the shared file instead of inlining
  framework detection tables.

- Differentiate test-anti-patterns (quick pragmatic review) from
  exp-test-smell-detection (deep formal audit with academic taxonomy) by updating
  descriptions and cross-referencing each other in When Not to Use sections.

- Update skill-validator to allow ../../shared/ file references while still
  blocking other parent-directory traversals. Add tests for the new rule.

* Switch from shared/ directories to hidden reference skills

Replace the plugin-level shared/ directories with non-invocable reference
skills (user-invocable: false) that other skills reference by name.

- Create platform-detection, filter-syntax, and dotnet-test-frameworks as
  hidden skills under plugins/dotnet-test/skills/. These contain the
  detection tables and syntax references previously duplicated across
  run-tests, mtp-hot-reload, and migrate-vstest-to-mtp.

- Create exp-dotnet-test-frameworks as a hidden skill under
  plugins/dotnet-experimental/skills/ for the experimental test analysis
  skills (exp-test-smell-detection, exp-assertion-quality, etc.).

- Update all consuming skills to reference these by skill name in backtick
  notation instead of file links.

- Revert the skill-validator ../../shared/ exception — no longer needed
  since all references now use the standard skill name mechanism.

* Merge exp-test-boilerplate-detection into exp-test-maintainability

exp-test-maintainability was only 6 calibration rules with no workflow.
exp-test-boilerplate-detection had the full 5-category detection workflow,
examples, calibration, and validation. Merge the boilerplate content into
exp-test-maintainability (the broader, more user-facing name) and add the
two unique maintainability rules (DisplayName guidance, DataRow vs
DynamicData preference) to Category 3.

- Replace exp-test-maintainability SKILL.md with the merged content
- Move test fixtures from exp-test-boilerplate-detection to exp-test-maintainability
- Merge eval.yaml scenarios (4 total: 2 from each original skill)
- Delete exp-test-boilerplate-detection skill and tests
- Update all cross-references in exp-test-smell-detection, dotnet-test-frameworks,
  exp-dotnet-test-frameworks, and CODEOWNERS

* Add cross-references to test-anti-patterns for deep mock and duplication analysis

Point users to exp-mock-usage-analysis from the Over-mocking entry and
to exp-test-maintainability from the Duplicate tests entry.

* Add exp-dotnet-test-frameworks to CODEOWNERS

* Improve run-tests SDK 10 MTP detection for blame-hang scenario

Inline the critical SDK 10 detection signal (global.json test.runner)
directly in Step 1 instead of deferring entirely to the platform-detection
skill. This makes the distinction between SDK 10 (no -- separator) and
SDK 8/9 (requires -- separator) more prominent.

Add a quick detection summary table, strengthen the Common Pitfalls entry
for SDK 10 with a blame-hang-timeout example, and keep the
platform-detection skill reference for the full detection logic.

* Improve skill activation keywords in descriptions

- exp-test-maintainability: Add 'suggest a better test structure',
  'consolidate similar test methods', 'convert copy-paste tests to
  data-driven parameterized tests' to match prompts like 'each new case
  needs a whole new method, suggest a better structure'.

- test-anti-patterns: Add 'review tests', 'find test problems', 'check
  test quality', 'audit tests for common mistakes' to match review-style
  prompts that don't use the word 'anti-pattern'.

- run-tests: Add 'hang timeout', 'blame-hang', 'blame-crash', 'TUnit'
  to match SDK 10 blame scenarios and TUnit filter scenarios that were
  intermittently not activating.
2026-04-08 13:47:09 +02:00

247 lines
11 KiB
YAML

scenarios:
- name: "Recommend data-driven patterns with display names for unclear parameters"
prompt: |
I need to add more test cases for our validation logic but each new case needs a whole new method.
Can you suggest a better structure?
```csharp
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Validation.Tests;
[TestClass]
public sealed class InputValidatorTests
{
[TestMethod]
public void Validate_EmptyString_ReturnsFalse()
{
var validator = new InputValidator();
var result = validator.Validate("");
Assert.IsFalse(result.IsValid);
Assert.AreEqual("Input cannot be empty", result.ErrorMessage);
}
[TestMethod]
public void Validate_TooShort_ReturnsFalse()
{
var validator = new InputValidator();
var result = validator.Validate("ab");
Assert.IsFalse(result.IsValid);
Assert.AreEqual("Input must be at least 3 characters", result.ErrorMessage);
}
[TestMethod]
public void Validate_TooLong_ReturnsFalse()
{
var validator = new InputValidator();
var result = validator.Validate(new string('x', 256));
Assert.IsFalse(result.IsValid);
Assert.AreEqual("Input must not exceed 255 characters", result.ErrorMessage);
}
[TestMethod]
public void Validate_ContainsSpecialChars_ReturnsFalse()
{
var validator = new InputValidator();
var result = validator.Validate("hello<script>");
Assert.IsFalse(result.IsValid);
Assert.AreEqual("Input contains invalid characters", result.ErrorMessage);
}
[TestMethod]
public void Validate_ValidInput_ReturnsTrue()
{
var validator = new InputValidator();
var result = validator.Validate("hello-world");
Assert.IsTrue(result.IsValid);
Assert.IsNull(result.ErrorMessage);
}
[TestMethod]
public void Validate_MinLength_ReturnsTrue()
{
var validator = new InputValidator();
var result = validator.Validate("abc");
Assert.IsTrue(result.IsValid);
Assert.IsNull(result.ErrorMessage);
}
[TestMethod]
public void Validate_MaxLength_ReturnsTrue()
{
var validator = new InputValidator();
var result = validator.Validate(new string('a', 255));
Assert.IsTrue(result.IsValid);
Assert.IsNull(result.ErrorMessage);
}
[TestMethod]
public void Validate_Null_ReturnsFalse()
{
var validator = new InputValidator();
var result = validator.Validate(null);
Assert.IsFalse(result.IsValid);
Assert.AreEqual("Input cannot be empty", result.ErrorMessage);
}
[TestMethod]
public void Validate_WhitespaceOnly_ReturnsFalse()
{
var validator = new InputValidator();
var result = validator.Validate(" ");
Assert.IsFalse(result.IsValid);
Assert.AreEqual("Input cannot be empty", result.ErrorMessage);
}
}
```
assertions:
- type: "output_matches"
pattern: "(DataRow|InlineData|TestCase|data.driven|parameteriz)"
- type: "output_matches"
pattern: "(DisplayName|display.name)"
- type: "exit_success"
rubric:
- "Identified that the invalid-input tests can be consolidated into a parameterized test with DataRow"
- "Identified that the valid-input tests can similarly be consolidated"
- "Recommended labeling parameterized test cases so that test runner output identifies which case failed"
- "Showed concrete refactored code with clear test case labels"
- "Acknowledged that the tests are otherwise well-structured (focused, clear assertions, proper naming)"
timeout: 120
- name: "Recognize well-maintained tests that need minimal changes"
prompt: |
Can you review these tests for maintainability? I want to make sure they'll be easy
to update as the codebase evolves.
```csharp
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Auth.Tests;
[TestClass]
public sealed class TokenServiceTests
{
[TestMethod]
[DataRow("user@example.com", "admin", DisplayName = "Admin user gets full-access token")]
[DataRow("viewer@example.com", "viewer", DisplayName = "Viewer gets read-only token")]
[DataRow("api@example.com", "service", DisplayName = "Service account gets API token")]
public void GenerateToken_ValidUser_ReturnsTokenWithCorrectRole(string email, string role)
{
var service = CreateTokenService();
var token = service.GenerateToken(email, role);
Assert.IsNotNull(token);
Assert.AreEqual(role, token.Role);
Assert.IsTrue(token.ExpiresAt > DateTime.UtcNow);
}
[TestMethod]
public void GenerateToken_ExpiredCredentials_ThrowsAuthException()
{
var service = CreateTokenService(clockOffset: TimeSpan.FromDays(-1));
Assert.ThrowsException<AuthenticationException>(
() => service.GenerateToken("user@example.com", "admin"));
}
[TestMethod]
public void RevokeToken_ValidToken_MarksRevoked()
{
var service = CreateTokenService();
var token = service.GenerateToken("user@example.com", "admin");
service.RevokeToken(token.Id);
Assert.IsTrue(service.IsRevoked(token.Id));
}
[TestMethod]
public void RevokeToken_AlreadyRevoked_IsIdempotent()
{
var service = CreateTokenService();
var token = service.GenerateToken("user@example.com", "admin");
service.RevokeToken(token.Id);
service.RevokeToken(token.Id); // second call
Assert.IsTrue(service.IsRevoked(token.Id));
}
private static TokenService CreateTokenService(TimeSpan? clockOffset = null)
{
var clock = clockOffset.HasValue
? new FakeClock(DateTimeOffset.UtcNow + clockOffset.Value)
: new FakeClock(DateTimeOffset.UtcNow);
return new TokenService(clock, new InMemoryTokenStore());
}
}
```
assertions:
- type: "output_matches"
pattern: "(well.maintained|good shape|clean|solid|maintainable|easy to maintain|no.*(major|significant))"
- type: "exit_success"
rubric:
- "Recognized that these tests are already well-maintained and need no major refactoring"
- "Acknowledged the existing helper method (CreateTokenService) as a good pattern"
- "Acknowledged that parameterized tests are well-labeled and effectively structured"
- "Did not recommend over-engineered patterns disproportionate to the test complexity"
- "If any suggestions were made, they were minor and clearly labeled as optional polish"
timeout: 120
# ==========================================================================
# Scenario 3: Heavy boilerplate — repeated construction and assertion patterns
# ==========================================================================
- name: "Detect repeated object construction and setup across test methods"
prompt: |
I feel like my test code has a lot of copy-paste. Can you analyze
my OrderService.Tests project and tell me what patterns are repeated
and where I could refactor?
setup:
files:
- path: "OrderService.Tests/OrderService.Tests.csproj"
source: "fixtures/heavy-boilerplate/OrderService.Tests/OrderService.Tests.csproj"
- path: "OrderService.Tests/OrderProcessorTests.cs"
source: "fixtures/heavy-boilerplate/OrderService.Tests/OrderProcessorTests.cs"
assertions:
- type: "output_matches"
pattern: "(factory|helper|extract|CreateProcessor|CreateOrder|shared|reuse|common)"
- type: "output_matches"
pattern: "(3|4|5|6|7|8|multiple|every|all|each|repeated)"
- type: "exit_success"
rubric:
- "Identified that the three-line FakeLogger/FakeEmailService/FakeInventoryService construction block is repeated in every OrderProcessorTests test method"
- "Identified that Order object construction with Items list is repeated across both test classes with very similar structure"
- "Suggested extracting a helper method or factory for creating the OrderProcessor with its dependencies"
- "Suggested extracting a helper for creating test Order objects with customizable properties"
- "Noted that OrderValidatorTests also repeats the same Order construction pattern as OrderProcessorTests, suggesting a cross-class shared helper"
- "Provided concrete before/after code showing how at least one extraction would look"
- "Did not recommend removing all duplication indiscriminately — acknowledged trade-offs between DRY and test readability"
timeout: 120
# ==========================================================================
# Scenario 4: Minimal boilerplate — tests are already well-structured
# ==========================================================================
- name: "Recognize tests with minimal boilerplate that need no refactoring"
prompt: |
Can you check my Calculator.Tests project for any repeated code patterns
that I should clean up? I have two test classes in there.
setup:
files:
- path: "Calculator.Tests/Calculator.Tests.csproj"
source: "fixtures/minimal-boilerplate/Calculator.Tests/Calculator.Tests.csproj"
- path: "Calculator.Tests/CalculatorTests.cs"
source: "fixtures/minimal-boilerplate/Calculator.Tests/CalculatorTests.cs"
assertions:
- type: "output_matches"
pattern: "(clean|minimal|good|well.structured|no.*(major|significant)|already|little.*(duplicat|boilerplate)|minor)"
- type: "exit_success"
rubric:
- "Recognized that CalculatorTests already uses parameterized data-driven tests and a shared field instance effectively"
- "Identified that ScientificCalculatorTests has individual test methods that could be consolidated into parameterized tests (e.g., SquareRoot positive/zero cases, Power squared/zero/negative exponent cases)"
- "Noted that the overall code is well-structured and any suggestions are minor improvements, not critical refactorings"
- "Did not recommend over-engineered patterns like base classes or shared fixtures for these simple test classes"
timeout: 120