# How to Write Effective Unit Tests for PowerToys Modules: A Complete Guide

> Learn to write effective unit tests for PowerToys modules using MSTest and WinAppDriver. Follow best practices for fast, reliable validation of your code.

- Repository: [Microsoft/PowerToys](https://github.com/microsoft/PowerToys)
- Tags: how-to-guide
- Published: 2026-02-25

---

**PowerToys modules use MSTest-based unit tests and WinAppDriver UI tests located in sibling `.UnitTests` or `.UITests` folders, following Arrange-Act-Assert patterns and descriptive naming conventions to ensure fast, deterministic validation of module logic.**

Writing effective unit tests for PowerToys modules requires adhering to the established conventions found in the microsoft/PowerToys repository. Each module maintains its own test suite under `src/modules/<module-name>/`, using standardized project structures and testing frameworks to validate both pure logic and UI interactions.

## Setting Up a Dedicated Test Project

Every PowerToys module ships with a dedicated test project located in a sibling folder following the naming convention `.UnitTests`, `.UITests`, or `.FuzzTests`. The test project file (`*.csproj`) must reference the module's main project and the appropriate testing packages.

For example, the FancyZones editor unit tests are defined in [`src/modules/fancyzones/FancyZonesEditor.UnitTests/FancyZonesEditor.UnitTests.csproj`](https://github.com/microsoft/PowerToys/blob/main/src/modules/fancyzones/FancyZonesEditor.UnitTests/FancyZonesEditor.UnitTests.csproj), which references MSTest for pure-logic validation. UI test projects like [`FancyZonesEditor.UITests.csproj`](https://github.com/microsoft/PowerToys/blob/main/src/modules/fancyzones/FancyZonesEditor.UITests/FancyZonesEditor.UITests.csproj) additionally include WinAppDriver packages for end-to-end automation.

## Structuring Tests with Arrange-Act-Assert

Effective unit tests for PowerToys modules follow the **Arrange-Act-Assert** pattern consistently across the codebase. This structure keeps tests readable and maintains a clear separation between setup, execution, and verification.

```csharp
[TestMethod]
public void When_GridLayoutIsCreated_ItHasCorrectDefaultValues()
{
    // Arrange
    var model = new GridLayoutModel();   // class under test

    // Act
    model.InitializeDefaults();

    // Assert
    Assert.AreEqual(expectedRows, model.RowCount);
    Assert.AreEqual(expectedColumns, model.ColumnCount);
}

```

This pattern appears throughout the repository, including in [[`GridLayoutModelTests.cs`](https://github.com/microsoft/PowerToys/blob/main/GridLayoutModelTests.cs)](https://github.com/microsoft/PowerToys/blob/main/src/modules/fancyzones/FancyZonesEditor.UnitTests/GridLayoutModelTests.cs) and [[`ResizeSizeTests.cs`](https://github.com/microsoft/PowerToys/blob/main/ResizeSizeTests.cs)](https://github.com/microsoft/PowerToys/blob/main/src/modules/imageresizer/tests/Models/ResizeSizeTests.cs).

### Naming Conventions and Organization

Use the **MethodUnderTest_StateUnderTest_ExpectedResult** format for test method names to clearly communicate intent. Prefix the test file with the class being exercised, such as [`GridLayoutModelTests.cs`](https://github.com/microsoft/PowerToys/blob/main/GridLayoutModelTests.cs) for tests targeting the `GridLayoutModel` class.

Apply the `[TestCategory]` attribute for logical grouping, particularly in UI test suites where categorization by feature area is essential.

## Keeping Tests Fast and Deterministic

Effective unit tests for PowerToys modules must execute quickly and produce consistent results without external dependencies. Do not access the file system, network, or UI layers unless the test is explicitly marked as a UI or fuzz test.

For I/O-heavy code, inject abstractions such as `IFileReader` and mock them with lightweight stubs. The Image Resizer tests demonstrate this approach using [[`TestDirectory.cs`](https://github.com/microsoft/PowerToys/blob/main/TestDirectory.cs)](https://github.com/microsoft/PowerToys/blob/main/src/modules/imageresizer/tests/Test/TestDirectory.cs) to create isolated temporary folders that clean up automatically after test execution.

## Leveraging Shared Test Utilities

PowerToys provides common helpers to reduce boilerplate across test suites:

- **FancyZonesEditorHelper**: Provides UI-test setup utilities, used extensively in [[`TemplateLayoutsTests.cs`](https://github.com/microsoft/PowerToys/blob/main/TemplateLayoutsTests.cs)](https://github.com/microsoft/PowerToys/blob/main/src/modules/fancyzones/FancyZonesEditor.UITests/TemplateLayoutsTests.cs)
- **AssertEx**: Offers fluent assertion extensions found in [[`AssertEx.cs`](https://github.com/microsoft/PowerToys/blob/main/AssertEx.cs)](https://github.com/microsoft/PowerToys/blob/main/src/modules/imageresizer/tests/Test/AssertEx.cs)

Reference these utilities rather than implementing custom test infrastructure to maintain consistency with existing unit tests for PowerToys modules.

## Testing Edge Cases and UI Scenarios

Comprehensive test coverage requires validation of failure paths and boundary conditions. Write explicit tests for null arguments, invalid enum values, and exception-throwing scenarios.

For automated input generation, PowerToys uses fuzz tests defined in projects like [`FancyZones.FuzzTests.csproj`](https://github.com/microsoft/PowerToys/blob/main/src/modules/fancyzones/FancyZones.FuzzTests/FancyZones.FuzzTests.csproj). These projects automatically generate malformed inputs to test module resilience.

### UI Testing with WinAppDriver

UI tests inherit from `UITestBase` and interact with the application through WinAppDriver:

```csharp
[TestClass]
public class TemplateLayoutsTests : UITestBase
{
    [TestMethod("FancyZonesEditor.Basic.ZoneNumber_Cancel")]
    [TestCategory("FancyZones Editor #6")]
    public void ZoneNumber_Cancel()
    {
        // Open the template layout dialog
        Session.Find<Button>(TestConstants.TemplateLayoutNames["Grid"]).Click();

        // Move the slider then cancel
        var slider = Session.Find<Custom>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.TemplateZoneSlider));
        slider.SendKeys("5");
        Session.Find<Button>(AccessibilityId.CancelButton).Click();

        // Verify the setting was not saved
        Assert.AreNotEqual(5, GetCurrentZoneCount());
    }
}

```

## Running Tests Locally and in CI

Execute tests using the PowerToys build scripts located in `tools/build/build.cmd`, which invoke `dotnet test` for all `*Tests.csproj` projects. The same tests run in Azure Pipelines as part of the *Run unit tests* step, ensuring continuous validation of all PowerToys modules.

## Documenting Test Intent and Data

Include a brief comment at the top of each test file describing the scenarios covered. Store test data assets, such as sample images for PowerRename validation, under a dedicated `testdata/` folder with clear attribution documentation, as implemented in [[`src/modules/powerrename/unittests/testdata/ATTRIBUTION.md`](https://github.com/microsoft/PowerToys/blob/main/src/modules/powerrename/unittests/testdata/ATTRIBUTION.md)](https://github.com/microsoft/PowerToys/blob/main/src/modules/powerrename/unittests/testdata/ATTRIBUTION.md).

## Summary

- Create test projects as sibling folders using the `.UnitTests` or `.UITests` naming convention
- Follow **Arrange-Act-Assert** patterns and descriptive naming like `When_State_ExpectResult`
- Keep tests fast by mocking I/O abstractions and avoiding file system or network calls in unit tests
- Reuse shared utilities such as `FancyZonesEditorHelper` and `AssertEx` for consistent test infrastructure
- Validate edge cases, null inputs, and exception paths alongside happy-path scenarios
- Execute tests via `build.cmd` or `dotnet test` to ensure compatibility with the CI pipeline

## Frequently Asked Questions

### What testing framework does PowerToys use for unit tests?

PowerToys uses the **MSTest** framework for pure-logic unit tests and **WinAppDriver** for UI automation tests. The MSTest framework provides attributes like `[TestMethod]` and `[TestCategory]` that appear consistently across modules such as FancyZones and Image Resizer.

### How do I test UI components in PowerToys modules?

Create a `.UITests` project that references WinAppDriver packages and inherits from `UITestBase`. Use the `Session.Find<T>()` method to locate UI elements by accessibility ID or control type, then interact with them programmatically. Reference [[`TemplateLayoutsTests.cs`](https://github.com/microsoft/PowerToys/blob/main/TemplateLayoutsTests.cs)](https://github.com/microsoft/PowerToys/blob/main/src/modules/fancyzones/FancyZonesEditor.UITests/TemplateLayoutsTests.cs) for working examples.

### Where should I place test data files in PowerToys modules?

Place test data assets in a `testdata/` subdirectory within your test project, accompanied by an [`ATTRIBUTION.md`](https://github.com/microsoft/PowerToys/blob/main/ATTRIBUTION.md) file documenting the source and licensing of the assets. This convention ensures test data remains organized and properly credited, as seen in the PowerRename module's test structure.

### How do I run PowerToys unit tests locally?

Use the repository's build script at `tools/build/build.cmd` to execute all test projects, or run `dotnet test` directly against individual `*.csproj` files. The build scripts automatically discover and execute all projects matching the `*Tests.csproj` pattern, matching the behavior of the Azure Pipelines CI environment.