MSTest 4.5 ile UWP ve WinUI 3’te UI Thread Testleri
UWP ve WinUI 3 uygulamalarını güvenilir biçimde test etmek için tek başına bir STA (single-threaded apartment) thread’i yetmiyor; testin kurulum, gövde ve temizlik adımlarının uygulamanın gerçek UI dispatcher’ına erişmesi gerekiyor. MSTest 4.5 ve Microsoft.Testing.Platform (MTP) 2.5 ile bu desen UWP ve WinUI 3 tarafında aynı şekilde kullanılabiliyor: testler uygulamanın kendi içinde, pencereyi ve dispatcher’ı sahiplenen süreçte çalışıyor.
Tipik bir WinUI 3 testi şu kadar sade kalıyor:
[UITestMethod]
public async Task GridCanBeCreatedOnTheUIThread()
{
await Task.Yield();
var grid = new Grid();
Assert.IsTrue(grid.DispatcherQueue.HasThreadAccess);
}
Hangi uygulama modelleri destekleniyor?
Desteklenen modeller şunlar: klasik ve modern UWP, paketli (packaged) veya paketsiz (unpackaged) WinUI 3 ve AppContainer kullanan WinUI host’ları. Burada paketleme ile güven seviyesi birbirinden ayrı iki karar: paketleme MSIX kimliği ve AUMID ile etkinleştirme getirir, güven seviyesi ise sürecin full trust mı yoksa AppContainer içinde mi çalışacağını belirler.
| Uygulama modeli | Kimlik ve güven | MTP test host yolu |
|---|---|---|
Klasik UWP (uap10.0) |
MSIX, AppContainer | Sidecar controller, UAP adapter/bootstrap varlıkları, AUMID ile etkinleştirme |
Modern UWP (UseUwp) |
MSIX, AppContainer | Sidecar controller, Native AOT host, AUMID ile etkinleştirme |
| Paketsiz WinUI 3 | Paket kimliği yok, full trust | Doğrudan apphost başlatma |
| Paketli WinUI 3 | MSIX, full trust | Sidecar controller, paket kaydı, AUMID ile etkinleştirme |
WinUI 3 packagedClassicApp + AppContainer |
MSIX, AppContainer | Sidecar controller ve tam paket SID’ine özel pipe yetkilendirmesi |
WinUI tarafında öneri, test edilen davranış paket kimliği, paketli etkinleştirme sözleşmeleri veya kurulu uygulamanın birebir davranışı gerektirmedikçe paketsiz modelle başlamak. UWP ise doğası gereği paketli ve sandbox’lı.
Paketli uygulamalar neden sidecar controller kullanıyor?
Paketli ve sandbox’lı uygulamalar ilk test aracı olarak başlatılamıyor. Bu nedenle MSTest.Sdk, test argümanlarını, iptal isteklerini, raporları, retry davranışını ve nihai çıkış kodunu sahiplenen full-trust bir sidecar controller başlatıyor; ardından testleri barındıran uygulamayı ayağa kaldırıyor. Bu akışta Microsoft.NET.Test.Sdk, vstest.console, UwpTestHostRuntimeProvider veya Visual Studio dağıtım runtime’ı kullanılmıyor.
Paketsiz uygulamalar doğrudan başlatılırken, paketli uygulamalar Microsoft.Testing.Extensions.PackagedApp ile kaydedilip AUMID üzerinden etkinleştiriliyor. AppContainer host’larında ise controller ve rapor pipe’larına yalnızca ilgili paket SID’i için erişim veriliyor; ALL APPLICATION PACKAGES gibi geniş bir yetkilendirme yapılmaması gerekiyor.
Makine tarafında bir ön koşul var: imzasız build çıktısı düzenini kaydedebilmek için Developer Mode veya benzeri bir sideloading politikası gerekiyor. AppContainer testlerinin yükseltilmemiş (non-elevated) çalıştırılması ve bu politikanın yalnızca geliştirici makinesinde değil CI ajanında da doğrulanması öneriliyor.
MSTest.Sdk 4.5 ve MTP’yi seçmek
Çözüm veya repo kökünde global.json ile MSTest.Sdk sürümünü sabitleyip.NET 10’un yerel dotnet test deneyimi için Microsoft.Testing.Platform’u seçiyorsunuz:
{
"test": {
"runner": "Microsoft.Testing.Platform"
},
"msbuild-sdks": {
"MSTest.Sdk": "4.5.0"
}
}
Aksi hâlde.NET 10, dotnet test için VSTest’i kullanıyor. MSTest.Sdk 4.5 paketi; MTP 2.5’i, uygulama modeline göre çalışan sidecar controller’ı, UWP adapter/bootstrap varlıklarını ve paketli uygulama launcher’ını içeriyor.
UWP projelerini yapılandırma
Modern UWP
Modern UWP projesinde test kurulumu oldukça kısa kalıyor:
<Project Sdk="MSTest.Sdk">
<PropertyGroup>
<TargetFramework>net10.0-windows10.0.26100.0</TargetFramework>
<UseUwp>true</UseUwp>
<PublishAot>true</PublishAot>
</PropertyGroup>
</Project>
Uygulamanın XAML, manifest, mimari ve Native AOT ayarları korunuyor. Ardından OnLaunched içinden etkinleştirme dizesi, üretilen MTP yardımcısına aktarılıyor:
using Microsoft.Testing.Extensions;
protected override async void OnLaunched(LaunchActivatedEventArgs args)
{
Window.Current.Activate();
string[] testArguments =
PackagedAppExtensions.GetTestApplicationArguments(args.Arguments);
Environment.ExitCode =
await MicrosoftTestingPlatformApplication.RunAsync(testArguments);
Exit();
}
Klasik UWP
Klasik uap10.0 projeleri mevcut yapılarını koruyor ve MSTest.Sdk‘yı MSBuild.Sdk.Extras ile birlikte import ediyor. MSTest 4.5; UAP uyumlu adapter’ı, üretilen bootstrap’ı, paketli uygulama launcher’ını ve TRX istemci varlıklarını içeriyor. Sidecar, .build.appxrecipe düzenini oluşturup bildirilen framework’leri kuruyor ve uygulamayı AUMID ile başlatıyor.
Klasik ve modern UWP derlemeleri hâlâ Visual Studio MSBuild/UWP araç zincirine ihtiyaç duyuyor; ancak artık onun VSTest runtime’ına veya dağıtım sağlayıcısına gerek kalmıyor. Bu nedenle UWP testleri Developer PowerShell for Visual Studio üzerinden çalıştırılıyor: önce masaüstü MSBuild araç zinciriyle derleyip sonra MTP hedefini çağırıyorsunuz.
msbuild.\MyUwpTests.sln /restore /p:Configuration=Release /p:Platform=x64
msbuild.\MyUwpTests.csproj /t:InvokeTestingPlatform /p:Configuration=Release /p:Platform=x64
Kendi kendine host eden WinUI 3 test uygulaması
WinUI 3 tarafında dispatcher’ı sağlayan ve MSTest’i kendi içinde çalıştıran bir uygulama kuruyorsunuz. Bunun için Windows,.NET 10 SDK ve Windows App SDK araçları (Visual Studio’nun “Windows application development” iş yükü ya da CLI ile derliyorsanız NuGet üzerinden) gerekiyor. Visual Studio’nun “Blank App, Packaged (WinUI 3 in Desktop)” şablonundan başlanıp, paketli form için Package.appxmanifest ve paket varlıkları korunuyor.
<Project Sdk="MSTest.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0-windows10.0.19041.0</TargetFramework>
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
<UseWinUI>true</UseWinUI>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Page Remove="UnitTestApp.xaml" />
<ApplicationDefinition Include="UnitTestApp.xaml" />
<ProjectCapability Include="TestContainer" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.WindowsAppSDK"
Version="1.8.251106002" />
</ItemGroup>
</Project>
ApplicationDefinition, minimal bir UnitTestApp.xaml dosyasını işaret ediyor:
<Application
x:Class="MyWinUiTests.UnitTestApp"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources />
</Application>
Uygulama, giriş noktasını bu ApplicationDefinition üzerinden sahipleniyor. MSTest.Sdk bu giriş noktasını algılayıp kendi rakip Main metodunu bastırıyor ve yeniden kullanılabilir bir MicrosoftTestingPlatformApplication.RunAsync yardımcısı üretiyor. Code-behind tarafı ise pencereyi oluşturup dispatcher’ı yayımlıyor ve bu yardımcıyı çağırıyor:
using Microsoft.UI.Xaml;
using Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer;
namespace MyWinUiTests;
public partial class UnitTestApp : Application
{
private Window? _window;
public UnitTestApp() => InitializeComponent();
protected override async void OnLaunched(LaunchActivatedEventArgs args)
{
_window = new Window();
_window.Activate();
UITestMethodAttribute.DispatcherQueue = _window.DispatcherQueue;
try
{
Environment.ExitCode = await MicrosoftTestingPlatformApplication.RunAsync(Environment.GetCommandLineArgs()[1..]);
}
finally
{
_window.Close();
Exit();
}
}
}
Akış her başlatmada aynı sırayla ilerliyor: Windows uygulamayı apphost ya da AUMID etkinleştirmesiyle başlatıyor, pencere oluşturulup etkinleştiriliyor, UITestMethodAttribute.DispatcherQueue yayımlanıyor, MTP testleri bu dispatcher üzerinde çalıştırıyor, sonuç Environment.ExitCode‘a atanıyor ve pencere kapatılarak uygulama sonlandırılıyor.
Environment.ExitCode ataması kritik: WinUI’nin ürettiği giriş noktası void döndürdüğü için bu atama olmadan başarısız bir test koşusu, build veya CI sistemine başarılı görünebiliyor.
Dispatcher’ı testle doğrulamak
Uygulama ve testler artık tek bir host oluşturduğu için TestInitialize, test gövdesi ve TestCleanup adımlarının UI dispatcher’ına erişimini doğrudan sınayabiliyorsunuz:
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml.Controls;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace MyWinUiTests;
[TestClass]
public sealed class ViewTests
{
private bool _initializedOnUiThread;
private bool _verifyCleanupOnUiThread;
[TestInitialize]
public async Task InitializeAsync()
{
await Task.Yield();
_initializedOnUiThread =
DispatcherQueue.GetForCurrentThread()?.HasThreadAccess == true;
}
[TestCleanup]
public async Task CleanupAsync()
{
await Task.Yield();
if (_verifyCleanupOnUiThread)
{
Assert.IsTrue(
DispatcherQueue.GetForCurrentThread()?.HasThreadAccess == true);
}
}
[UITestMethod]
public async Task ControlCanBeCreatedAfterAsyncInitialization()
{
_verifyCleanupOnUiThread = true;
await Task.Yield();
var grid = new Grid();
Assert.IsTrue(_initializedOnUiThread);
Assert.IsTrue(grid.DispatcherQueue.HasThreadAccess);
}
}
Buradaki ayrım önemli: [STATestMethod] bir STA thread’i sağlayabilir ama WinUI dispatcher’ı oluşturmaz. [UITestMethod] ise setup ve cleanup dahil olmak üzere her testin tüm MSTest çağrısını dispatcher üzerine gönderiyor.
Dağıtım modeline göre farklar ve çalıştırma
MSTest.Sdk runner kurulumunu iki model arasında ortak tutuyor; yalnızca proje dosyasındaki delta değişiyor.
Paketsiz WinUI 3 (varsayılan tercih)
Şu özellikleri ekliyor, MSIX manifest ve paket varlığı öğelerini dahil etmiyorsunuz:
<PropertyGroup>
<WindowsPackageType>None</WindowsPackageType>
<EnableMsixTooling>false</EnableMsixTooling>
</PropertyGroup>
Çıktı standart bir çalıştırılabilir apphost oluyor; Microsoft.Testing.Platform normal başlatma yolunu kullanıyor ve VSTest’in appx runtime sağlayıcısı devreye girmiyor.
Paketli full-trust WinUI 3 (kimlik gerektiğinde)
Bu iki paketsiz geçersiz kılmayı kaldırıyor, özellikle <WindowsPackageType>None</WindowsPackageType> satırını projede bırakmıyorsunuz. Şablonun Package.appxmanifest dosyası ve paket varlıkları korunuyor. Çıktı MSIX kimliğine sahip olduğundan Windows’un paket düzenini kaydetmesi ve test host’unu AUMID ile etkinleştirmesi gerekiyor. Bu model 10.0.19041.0 veya üzeri bir Windows TFM’i ve imzasız build çıktısı için Developer Mode ya da benzeri bir sideloading politikası istiyor.
Paketli WinUI projelerinde MSTest.Sdk, paketli uygulama launcher’ını ekleyip kaydediyor. TESTINGPLATFORM_PACKAGEDAPP_LAUNCHER değişkenini ayarlamadan bırakmanız öneriliyor: varsayılan auto değeri, paketli başlatma yolunu yalnızca uygulamayı tanımlayan uygun bir AppxManifest.xml varsa devreye alıyor; aksi hâlde controller yeniden başlatma ve dağıtım kopyalama maliyeti olmayan daha hızlı normal yolu koruyor. always ve never geçersiz kılmaları ise daha nadir senaryolar için dokümantasyonda anlatılıyor.
AppContainer olarak yapılandırılmış WinUI 3
Paketli bir WinUI 3 uygulaması varsayılan olarak full trust çalışıyor. Desteklenen packagedClassicApp + AppContainer kurulumunu test etmek için paketli WinUI host’u korunuyor ve manifestte uap10:TrustLevel="appContainer" ayarlanıyor. Host normal süreç argümanlarını almaya devam ederken MTP, controller, iptal, TRX, HangDump ve Retry pipe’larına yalnızca o paket SID’i için erişim veriyor.
Bu şekil, yükseltilmemiş bir Developer PowerShell üzerinden MTP MSBuild hedefiyle çalıştırılıyor:
dotnet build.\MyAppContainerTests.csproj -c Release -p:Platform=x64
dotnet msbuild.\MyAppContainerTests.csproj /t:InvokeTestingPlatform /p:Configuration=Release /p:Platform=x64
Çalıştırma
Full-trust WinUI tarafında her iki dağıtım modeli için dotnet run kullanılabiliyor; bu komut, pencereyi ve dispatcher’ı sahiplenen süreç içinde Microsoft.Testing.Platform’u barındıran apphost’u başlatıyor:
dotnet run
.NET 10’un yerel MTP runner’ı her iki model için dotnet test desteği de sunuyor:
dotnet test --project.\MyWinUiTests.csproj -c Release -a x64
Paketsiz uygulamada üretilen apphost doğrudan da başlatılabiliyor. Ancak dotnet exec kullanmamak gerekiyor; bu yöntem araya dotnet.exe‘yi soktuğu için WinUI kaynak yüklemesini bozabiliyor.
Test sırasında kısa süre bir pencere görünüyor ve konsol özeti yazdırıyor:
Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: 63ms - MyWinUiTests.dll (net10.0-windows10.0.19041.0)
Paketli geliştirme düzeni koşudan sonra kayıtlı kalabiliyor. Gerektiğinde manifestteki Identity adıyla kaldırılıyor:
Get-AppxPackage -Name '<packageidentityname>' |
Remove-AppxPackage -PreserveApplicationData
CI ajanında doğrulama
Paketli bir test host’u için geçen bir derleme yalnızca ilk adım. Gerçek CI ajan imajınızda şunları kontrol etmek gerekiyor: paketi kaydedebilecek bir kullanıcı bağlamı, gerekli Developer Mode veya sideloading politikası ve paketin bildirdiği framework’ler. Aksi hâlde UWP framework paketleri ya da Windows App SDK runtime’ı zaten kurulu bir geliştirici makinesi, yalnızca temiz bir build ajanında ortaya çıkacak bir eksiği gizleyebiliyor.
Framework bağımlı WinUI 3 uygulamaları ajanda eşleşen Windows App SDK runtime’ına ihtiyaç duyuyor. Self-contained bir WinUI 3 derlemesi bu makine gereksinimini ortadan kaldırabiliyor; ancak testin, CI’da çalışacak paket modeliyle birebir aynı şekilde yapılması öneriliyor. Bu doğrulamayı zorunlu bir gate hâline getirmeden önce kendi paketli uygulamanızda ve CI ajanınızda temiz, geçen bir çıkış kodu görmek işinizi kolaylaştırır.
Başlangıç kontrol listesi
global.jsoniçinde Microsoft.Testing.Platform’u seçin ve MSTest.Sdk 4.5 kullanın.- Modern UWP için
UseUwpvePublishAotayarlayın; klasik UWP’deMSTest.Sdk‘yı mevcut projeye import edin; WinUI 3 içinUseWinUIayarlayıp paketli/paketsiz dağıtımı seçin. - Üretilen
MicrosoftTestingPlatformApplication.RunAsyncyardımcısınıOnLaunchediçinden çağırın. Modern UWP argümanlarıPackagedAppExtensions.GetTestApplicationArgumentsile geri kazanır; WinUI süreç argümanlarını kullanır. Gereken yerde UI dispatcher’ı yayımlayın. - Full-trust WinUI’yi
dotnet runveyadotnet testile; UWP ve AppContainer WinUI’yi ise yükseltilmemiş Developer PowerShell’denInvokeTestingPlatformMSBuild hedefiyle çalıştırın. - Paketli ve AppContainer modellerinde test ajanında Developer Mode (veya sideloading politikanızı) doğrulayın ve yükseltilmemiş çalıştırın.
Özetle: aynı MSTest yaşam döngüsü ve UI dispatcher testleri UWP ile WinUI 3 arasında yeniden kullanılabiliyor; paketsiz WinUI doğrudan apphost ile başlıyor, paketli ve AppContainer host’ları ise MTP tarafından kaydedilip AUMID ile etkinleştiriliyor. Geriye, CI ajanlarınızın çalıştıracağı paket modelini, gerekli framework’leri, güven seviyesini ve makine politikasını doğrulamak kalıyor.
Kaynaklar ve İleri Okuma
- UWP and WinUI 3 apps: UI testing with MSTest (.NET Blog, Amaury Levé)
- UWP ve WinUI test rehberi (microsoft/testfx dokümantasyonu)
- MSTest ile birim testi dokümantasyonu
- Microsoft.Testing.Platform genel bakış
- MSTest.Sdk tanıtım yazısı
- .NET test framework’lerinde Microsoft.Testing.Platform desteği
- Microsoft.Testing.Extensions.PackagedApp (NuGet)
- Örnek projeler: ClassicUwpMtpApp, UwpMtpApp, WinUIMtpUnpackagedApp, WinUIMtpPackagedApp, WinUIMtpAppContainerApp
- Microsoft.Testing.Platform ile Test Raporlama Rehberi







Yorum gönder