Files
AmiReel/AmiReel.WinUI/.github/instructions/testing.instructions.md
T

9.5 KiB

description, applyTo
description applyTo
Unit testing standards, test project setup, naming, build & run commands **/*Tests.cs, **/*Test.cs, **/*.Tests.csproj

Testing — Unit Tests, Build & Run

Every public method and class must have corresponding unit tests. Tests are not optional.


1. Test Framework & Project Setup

Component Package Purpose
Test Framework MSTest Test runner & assertions
Mocking Moq Mock dependencies
UI Testing Microsoft.Windows.Apps.Test WinUI UI automation (optional)

Test Project Setup

Create a test project alongside the main project:

<SolutionRoot>/
  <ProjectName>/           ← Main app project
  <ProjectName>.Tests/     ← Unit test project

Test project .csproj should reference the main project:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <!-- Match TargetFramework to the main project's .csproj -->
    <TargetFramework><!-- same as the main project's .csproj TargetFramework --></TargetFramework>
    <UseWinUI>true</UseWinUI>
    <Nullable>enable</Nullable>
    <IsPackable>false</IsPackable>
  </PropertyGroup>

  <ItemGroup>
    <!-- Use latest stable versions; do not hard-code version numbers in instructions -->
    <PackageReference Include="MSTest.TestAdapter" Version="*" />
    <PackageReference Include="MSTest.TestFramework" Version="*" />
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="*" />
    <PackageReference Include="Moq" Version="*" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\<ProjectName>\<ProjectName>.csproj" />
  </ItemGroup>
</Project>

2. Test Writing Rules

What to Test

  • All public methods in ViewModels, Services, Helpers, and Models.
  • Edge cases — null inputs, empty collections, boundary values.
  • Error paths — exception handling, invalid state transitions.
  • Business logic — calculations, transformations, state management.

What NOT to Test (Directly)

  • XAML layout / visual rendering (use UI tests for that).
  • Framework internals (e.g., InitializeComponent()).
  • Private methods — test them indirectly through public methods.

Test Naming Convention

Use the pattern: MethodName_Scenario_ExpectedResult

[TestMethod]
public void CalculateTotal_WithEmptyCart_ReturnsZero() { }

[TestMethod]
public void LoadDataAsync_WhenServiceThrows_SetsErrorState() { }

[TestMethod]
public async Task SaveAsync_WithValidInput_ReturnsTrue() { }

Test Structure (AAA Pattern)

Every test follows Arrange → Act → Assert:

[TestMethod]
public void Add_TwoPositiveNumbers_ReturnsSum()
{
    // Arrange
    var calculator = new Calculator();

    // Act
    int result = calculator.Add(2, 3);

    // Assert
    Assert.AreEqual(5, result);
}

ViewModel Testing Example

[TestMethod]
public async Task LoadItemsAsync_OnSuccess_PopulatesItems()
{
    // Arrange
    var mockService = new Mock<IDataService>();
    mockService
        .Setup(s => s.GetItemsAsync())
        .ReturnsAsync(new List<Item> { new("Test") });

    var viewModel = new MainViewModel(mockService.Object);

    // Act
    await viewModel.LoadItemsAsync();

    // Assert
    Assert.AreEqual(1, viewModel.Items.Count);
    Assert.IsFalse(viewModel.IsLoading);
}

3. Test Organization

File Structure

Mirror the main project's folder structure (defined in winui-best-practices) in the test project. As the main project grows with subfolders under ViewModels/, Services/, Views/, etc., the test project must grow organically in the same way. This alignment enables on-demand test runs scoped to the area you changed:

<ProjectName>/                    <ProjectName>.Tests/
  Models/                           Models/
    User.cs                           UserTests.cs
  ViewModels/                       ViewModels/
    MainViewModelTests.cs             MainViewModelTests.cs
    Settings/                         Settings/
      ThemeViewModel.cs                 ThemeViewModelTests.cs
  Services/                         Services/
    DataService.cs                    DataServiceTests.cs
    Auth/                             Auth/
      AuthService.cs                    AuthServiceTests.cs
  Helpers/                          Helpers/
    StringHelper.cs                   StringHelperTests.cs
  Converters/                       Converters/
    BoolToVisibilityConverter.cs      BoolToVisibilityConverterTests.cs

One Test Class per Class Under Test

namespace <RootNamespace>.Tests.ViewModels;

[TestClass]
public class MainViewModelTests
{
    // All tests for MainViewModel go here
}

Running Tests On-Demand

After a change, run only the tests related to the affected area instead of the full suite. Detect the platform first (matching the convention in .github/agents/Agents.md):

# Run from the test project folder
cd <ProjectName>.Tests

# Detect platform once per session (AMD64 -> x64; ARM64/x86 unchanged)
$arch = $env:PROCESSOR_ARCHITECTURE
$Platform = if ($arch -eq 'AMD64') { 'x64' } else { $arch }

# Run tests for a specific class
dotnet test -c Debug -p:Platform=$Platform --filter "FullyQualifiedName~MainViewModelTests"

# Run a single test
dotnet test -c Debug -p:Platform=$Platform --filter "FullyQualifiedName~MainViewModelTests.LoadItemsAsync_OnSuccess_PopulatesItems"

# Run all tests in a namespace (e.g., all ViewModel tests)
dotnet test -c Debug -p:Platform=$Platform --filter "FullyQualifiedName~Tests.ViewModels"

# Run tests in a subfolder namespace (e.g., only Settings ViewModels)
dotnet test -c Debug -p:Platform=$Platform --filter "FullyQualifiedName~Tests.ViewModels.Settings"

# Run the full suite (for cross-cutting changes)
dotnet test -c Debug -p:Platform=$Platform

---

## 4. Test-Specific Commands

For general build and register commands, see **Build, Run & Deploy** in `.github/agents/Agents.md`.
For on-demand test filtering, see **Running Tests On-Demand** above.

Below are additional test commands:

### Build Only the Test Project

```powershell
cd <ProjectName>.Tests
$arch = $env:PROCESSOR_ARCHITECTURE
$Platform = if ($arch -eq 'AMD64') { 'x64' } else { $arch }
dotnet build -c Debug -p:Platform=$Platform

Run Tests with Verbose Output

dotnet test -c Debug -p:Platform=$Platform --verbosity normal

5. Agent Workflow for Tests

When you write or modify code, follow this sequence:

  1. Implement the feature or fix in the main project.
  2. Write unit tests for every new/changed public method.
  3. Build — see Build, Run & Deploy in .github/agents/Agents.md. Fix all errors and warnings.
  4. Run testsdotnet test -c Debug -p:Platform=$Platform (from the test project folder; detect $Platform as shown above) and ensure all pass.
  5. Review — Confirm tests cover the happy path, edge cases, and error cases.

When Modifying Existing Code

  1. Run existing tests first to establish a baseline.
  2. Make the code change.
  3. Run tests again — fix any failures.
  4. Add new tests if the change introduces new behaviour.

Coverage Goals

  • Aim for 80%+ code coverage on business logic (ViewModels, Services).
  • 100% coverage of utility/helper methods.
  • UI code-behind is exempt from unit test coverage (tested via integration/UI tests).

6. Common Test Pitfalls

Pitfall Fix
Test depends on another test's state Each test must be fully independent
Testing multiple things in one test One assertion per logical concept
Tests pass but don't actually verify anything Always have meaningful assertions
Mocking too much Mock only external dependencies, not the class under test
Testing implementation details Test behaviour and outcomes, not internal method calls
Async tests without await Always await async methods and use async Task return type

Validation

  • Build & run tests — see Build, Run & Deploy in .github/agents/Agents.md.
  • Verify all tests pass — zero failures, zero skipped without justification.
  • Verify naming follows MethodName_Scenario_ExpectedResult pattern.
  • Verify AAA structure (Arrange/Act/Assert) in every test method.
  • Confirm coverage goals: 80%+ on ViewModels/Services, 100% on helpers.

Must Read & Research

Agent Rule: Before writing or modifying tests, you must fetch and review the relevant references below using fetch_webpage. Apply what you learn — do not skip this step.

# Reference When to consult
1 Unit testing C# with MSTest Setting up test project, writing first tests, MSTest attributes
2 Unit testing best practices .NET Every time you write tests — naming, structure, AAA pattern
3 Moq Quickstart Mocking interfaces, setting up Returns/Throws, verifying calls
4 FluentAssertions Documentation Writing expressive assertions (Should().Be(), collections, exceptions)
5 dotnet test CLI Running tests from terminal, filtering, verbosity options
6 Test Explorer in Visual Studio Debugging tests, viewing coverage, understanding test output