Development ⚠️ GridPlacement 6.0 documentation is in active development. APIs and content may change, and the site may be temporarily unstable.

GridPlacement 6.0 C# Test Parameterization Guidelines

This guide explains when and how to use parameterized tests (xUnit [Theory] + InlineData) in the GridPlacement 6.0 C# suites.

It is intentionally short and opinionated.


When Parameterization Is Worth It (≥ 7/10)

Use [Theory] when all of the following are true:

  • Same behavior shape
    • The test body is identical for each case.
    • Only the input data and expected outcome differ.
  • Clearer coverage map
    • InlineData rows make scenarios easier to scan than many tiny Facts.
  • Easy to extend
    • Adding new cases is just adding more InlineData rows.
  • No complex branching in the test body
    • Avoid if / switch per-case logic.

Examples in this repo

  • GridMathTests
    • Distance, region, and bounds checks with different integer inputs.
  • GridPositionTests
    • Manhattan/Chebyshev distances and adjacency matrices.
  • BoundsValidationTest
    • Area and tile-count calculations.
  • PhysicsMatchingUtilsTest
    • Bitmask→layers and mask matching shapes.
  • PlaceableCatalogTests
    • TryGet_ReturnsExpectedResultBasedOnId for existing vs unknown IDs.
  • PlaceableSequenceTests
    • GetTier_IndexOutOfRange_ThrowsArgumentOutOfRangeException for invalid indices.

These tests are good parameterization targets because:

  • The intent is the same across rows.
  • The behavior is essentially a pure function.
  • The assertion pattern does not change.

When To Prefer Separate [Fact] Tests

Keep individual [Fact] tests when any of these hold:

  • Different behaviors or domains
    • Each test exercises a different branch of business logic.
    • Example: start vs cancel vs complete flows in ManipulationServiceTests.
  • Different assertion shapes
    • One test checks events, another checks exceptions, another checks state.
  • High-level scenario or integration tests
    • Smoke tests, workflows, or end-to-end adapter paths.
  • Parameterization would require branching
    • If you need if (case == "X") inside the test, prefer multiple Facts.

Examples kept as Facts on purpose

  • ManipulationStateTests / ManipulationServiceTests
    • Lifecycle, events, validation, and per-mode behaviors.
  • SelectionAdapterTests
    • Catalog init, empty catalog handling, category filters, end-to-end build mode.
  • PlaceableCollectionTests
    • Many distinct behaviors (add/remove, categories, metadata), each short and focused.
  • Godot smoke tests
    • Minimal service-level scenarios; typically 1–2 tests per class.

Practical Heuristics

When you consider parameterization, ask:

  1. Does this reduce duplication without hiding intent?
  2. Would a future engineer understand the behavior from InlineData alone?
  3. Will adding more scenarios stay simple?
  4. Does the test body stay branch-free and readable?

If the answer is yes to most of these, parameterize. If not, keep (or split into) small, explicit [Fact] tests.


Style Notes (for GridPlacement 6.0)

  • Prefer descriptive test method names over cryptic data.
  • Keep InlineData values small and readable (ints/strings/booleans over complex objects).
  • For more complex data, favor simple helper methods in the same test class instead of trying to stuff everything into InlineData.
  • Do not parameterize just to “reduce line count” if it hurts clarity.

These guidelines are intentionally conservative: we want parameterization where it meaningfully improves maintainability and coverage, not everywhere by default.