How to Write Effective Unit Tests for PowerToys Modules: A Complete Guide
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, which references MSTest for pure-logic validation. UI test projects like 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.
[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/src/modules/fancyzones/FancyZonesEditor.UnitTests/GridLayoutModelTests.cs) and [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 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/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/src/modules/fancyzones/FancyZonesEditor.UITests/TemplateLayoutsTests.cs) - AssertEx: Offers fluent assertion extensions found in [
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. 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:
[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).
Summary
- Create test projects as sibling folders using the
.UnitTestsor.UITestsnaming 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
FancyZonesEditorHelperandAssertExfor consistent test infrastructure - Validate edge cases, null inputs, and exception paths alongside happy-path scenarios
- Execute tests via
build.cmdordotnet testto 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/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 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →