|
| 1 | +--- |
| 2 | +description: "Guidelines for writing XAML unit tests in the Controls.Xaml.UnitTests project" |
| 3 | +--- |
| 4 | + |
| 5 | +# XAML Unit Testing Guidelines |
| 6 | + |
| 7 | +This guide provides conventions for writing XAML unit tests in `src/Controls/tests/Xaml.UnitTests/`. |
| 8 | + |
| 9 | +## 1. File Naming and Location for Issue Tests |
| 10 | + |
| 11 | +When writing a test for a GitHub issue: |
| 12 | + |
| 13 | +- **Location**: Place the test file in the `Issues/` folder |
| 14 | +- **Naming convention**: `MauiXXXXX.xaml` and `MauiXXXXX.xaml.cs` (where XXXXX is the GitHub issue number) |
| 15 | + |
| 16 | +**Example:** |
| 17 | +``` |
| 18 | +src/Controls/tests/Xaml.UnitTests/Issues/ |
| 19 | +├── Maui12345.xaml |
| 20 | +└── Maui12345.xaml.cs |
| 21 | +``` |
| 22 | + |
| 23 | +## 2. Basic Test Pattern |
| 24 | + |
| 25 | +Most unit tests will: |
| 26 | +1. Check that the XAML page can be created/inflated |
| 27 | +2. Perform some assertions on the created page |
| 28 | +3. Apply to all `[XamlInflator]` test variations (runtime, XamlC, SourceGen) |
| 29 | + |
| 30 | +**XAML file (Maui12345.xaml):** |
| 31 | +```xaml |
| 32 | +<?xml version="1.0" encoding="utf-8" ?> |
| 33 | +<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" |
| 34 | + xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" |
| 35 | + x:Class="Microsoft.Maui.Controls.Xaml.UnitTests.Maui12345"> |
| 36 | + <!-- Your test content --> |
| 37 | +</ContentPage> |
| 38 | +``` |
| 39 | + |
| 40 | +**Code-behind (Maui12345.xaml.cs):** |
| 41 | +```csharp |
| 42 | +namespace Microsoft.Maui.Controls.Xaml.UnitTests; |
| 43 | + |
| 44 | +public partial class Maui12345 : ContentPage |
| 45 | +{ |
| 46 | + public Maui12345() |
| 47 | + { |
| 48 | + InitializeComponent(); |
| 49 | + } |
| 50 | + |
| 51 | + [TestFixture] |
| 52 | + class Tests |
| 53 | + { |
| 54 | + [Test] |
| 55 | + public void MyTest([Values] XamlInflator inflator) |
| 56 | + { |
| 57 | + var page = new Maui12345(inflator); |
| 58 | + Assert.That(page, Is.Not.Null); |
| 59 | + // Additional assertions |
| 60 | + } |
| 61 | + } |
| 62 | +} |
| 63 | +``` |
| 64 | + |
| 65 | +**Note**: Only add `[SetUp]` and `[TearDown]` methods if your test requires special initialization (e.g., setting up `DispatcherProvider` or `Application.Current`). Most tests don't need them. |
| 66 | + |
| 67 | +## 3. Testing IL Correctness (XamlC) or Source Generation |
| 68 | + |
| 69 | +When the test specifically validates compile-time behavior for a specific inflator: |
| 70 | + |
| 71 | +**XamlC (IL generation):** |
| 72 | +```csharp |
| 73 | +[Test] |
| 74 | +public void MyTest([Values] XamlInflator inflator) |
| 75 | +{ |
| 76 | + if (inflator == XamlInflator.XamlC) |
| 77 | + Assert.Throws<BuildException>(() => MockCompiler.Compile(typeof(Maui12345))); |
| 78 | + else |
| 79 | + { |
| 80 | + var page = new Maui12345(inflator); |
| 81 | + Assert.That(page, Is.Not.Null); |
| 82 | + } |
| 83 | +} |
| 84 | +``` |
| 85 | + |
| 86 | +**Source Generation:** |
| 87 | +```csharp |
| 88 | +using static Microsoft.Maui.Controls.Xaml.UnitTests.MockSourceGenerator; |
| 89 | + |
| 90 | +[Test] |
| 91 | +public void MyTest([Values] XamlInflator inflator) |
| 92 | +{ |
| 93 | + if (inflator == XamlInflator.SourceGen) |
| 94 | + { |
| 95 | + var result = CreateMauiCompilation() |
| 96 | + .WithAdditionalSource( |
| 97 | +""" |
| 98 | +namespace Microsoft.Maui.Controls.Xaml.UnitTests; |
| 99 | +
|
| 100 | +[XamlProcessing(XamlInflator.Runtime, true)] |
| 101 | +public partial class Maui12345 : ContentPage |
| 102 | +{ |
| 103 | + public Maui12345() => InitializeComponent(); |
| 104 | +} |
| 105 | +""") |
| 106 | + .RunMauiSourceGenerator(typeof(Maui12345)); |
| 107 | + Assert.That(result.Diagnostics, Is.Empty); // or Is.Not.Empty for error cases |
| 108 | + } |
| 109 | + else |
| 110 | + { |
| 111 | + var page = new Maui12345(inflator); |
| 112 | + Assert.That(page, Is.Not.Null); |
| 113 | + } |
| 114 | +} |
| 115 | +``` |
| 116 | + |
| 117 | +## 4. Handling Invalid Code Generation |
| 118 | + |
| 119 | +When testing scenarios where code generation is **expected to fail** or produce invalid code, use special file extensions to prevent build failures: |
| 120 | + |
| 121 | +| Extension | Behavior | Use Case | |
| 122 | +|-----------|----------|----------| |
| 123 | +| `.rt.xaml` | Runtime inflation only | XamlC/SourceGen should fail, but runtime should work | |
| 124 | +| `.rtsg.xaml` | Runtime + SourceGen only | XamlC should fail | |
| 125 | +| `.rtxc.xaml` | Runtime + XamlC only | SourceGen should fail | |
| 126 | + |
| 127 | +**Why?** These extensions prevent the compiler and build tasks from processing the XAML file, allowing your test to execute and verify the expected behavior. |
| 128 | + |
| 129 | +**Example:** |
| 130 | +``` |
| 131 | +Issues/ |
| 132 | +├── Maui12345.rt.xaml # Only inflated at runtime (compiler/sourcegen skip) |
| 133 | +└── Maui12345.xaml.cs |
| 134 | +``` |
| 135 | + |
| 136 | +## Quick Reference |
| 137 | + |
| 138 | +| Scenario | Location | Naming | Special Extension | |
| 139 | +|----------|----------|--------|-------------------| |
| 140 | +| Issue test | `Issues/` | `MauiXXXXX.*` | None (unless invalid codegen) | |
| 141 | +| Invalid XamlC | `Issues/` | `MauiXXXXX.rt.xaml` | `.rt.xaml` | |
| 142 | +| Invalid SourceGen | `Issues/` | `MauiXXXXX.rt.xaml` or `.rtxc.xaml` | Depends on what should fail | |
| 143 | +| Test XamlC IL | Anywhere | Any | Use `MockCompiler` | |
| 144 | +| Test SourceGen | Anywhere | Any | Use `MockSourceGenerator` | |
| 145 | + |
| 146 | +## Related Documentation |
| 147 | + |
| 148 | +- `.github/copilot-instructions.md` - General MAUI development guidelines |
| 149 | +- `.github/instructions/uitests.instructions.md` - UI testing guidelines (different from XAML unit tests) |
0 commit comments