MSTest 4 Rehberi: TestClass, DataRow/DynamicData, Assert API'si, TestContext, Paralellik, Analyzer'lar ve TUnit'e Kısa Bakış
MSTest 4'te kaldırılan [ExpectedException], method'un herhangi bir satırındaki aynı tipte exception'ı başarı sayıyordu; yükseltmede Assert.ThrowsExactly ve ParamName kontrolü, yıllardır yanlış sebeple yeşil kalan bir testi ve hiç yazılmamış bir kuralı ortaya çıkardı. MSTest.Sdk ve MTP, DataRow'da decimal tuzağı, DynamicData, Assert API'si, TestContext, paralellik, analyzer'lar, TUnit'e kısa bakış ve serinin kapanışı.
Sürüm notu: Bu yazıdaki kodlar .NET 10, MSTest 4.5.3 (MSTest.Sdk; karşılaştırma için 3.11.1) ve TUnit 1.85.0 ile derlenip denenmiştir.
Önce Tahmin Et
Serinin son yazısında MSTest'e bakıyoruz; Microsoft'un kendi test framework'ü ve kurumsal projelerde, özellikle Visual Studio template'leriyle başlamış eski projelerde sık karşına çıkıyor. Sitedeki serilerin ortak örnek uygulaması Bookstore'da (bir online kitapçı) kampanya kurallarını test eden eski bir MSTest projesi var. MSTest 3.11.1 ile:
[TestClass]
public class CampaignTests
{
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void Discount_above_half_is_rejected()
{
Campaign.StartNow("BLACKFRIDAY", 0.60m, TimeSpan.FromDays(3), TimeProvider.System);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void Campaign_longer_than_90_days_is_rejected()
{
Campaign.StartNow("SUMMER", 0.55m, TimeSpan.FromDays(120), TimeProvider.System);
}
}
İki test de yeşil; derleme yalnız iki analyzer uyarısı veriyor:
CampaignTests.cs(10,17): warning MSTEST0006: Prefer 'Assert.ThrowsExactly/ThrowsExactlyAsync' over '[ExpectedException]' (https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0006)
CampaignTests.cs(17,17): warning MSTEST0006: Prefer 'Assert.ThrowsExactly/ThrowsExactlyAsync' over '[ExpectedException]' (https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0006)
Kampanyalar Catalog module'ündeki Campaign.StartNow ile oluşuyor: indirim %50'yi geçemez, kampanya 90 günden uzun süremez. Proje MSTest 4.5.3'e yükseltilecek. İkinci test 90 gün kuralını gerçekten koruyor mu?
Cevap: ExpectedException Hangi Satırın Neden Attığını Bilmiyor
MSTest 4.5.3'e geçince proje derlenmiyor; ExpectedException attribute'u 4.0'da kaldırıldı:
CampaignTests.cs(9,6): error CS0246: The type or namespace name 'ExpectedException' could not be found (are you missing a using directive or an assembly reference?)
CampaignTests.cs(16,6): error CS0246: The type or namespace name 'ExpectedException' could not be found (are you missing a using directive or an assembly reference?)
MSTEST0006'nın code fix'inin yaptığı gibi, method'un gövdesini Assert.ThrowsExactly'nin içine taşıdık ve iki test yine yeşil kaldı. Ama artık exception'ı bir değişkene alabiliyoruz; ikinci teste hangi parametrenin reddedildiğini sorunca:
failed Campaign_longer_than_90_days_is_rejected (5ms)
Assertion failed. Expected strings to be equal.
Strings have different lengths (expected: 8, actual: 12) and differ at 1 location(s). First difference at index 1.
expected: "duration"
actual: "discountRate"
difference: --^
Assert.AreEqual("duration", ex.ParamName)
Exception süreden değil, indirimden geliyormuş: test birinci testten kopyalanırken indirim 0.55 kalmış. İndirimi 0.20 yapınca gerçek durum çıktı:
failed Campaign_longer_than_90_days_is_rejected (2ms)
Assertion failed. Expected exception of exact type ArgumentOutOfRangeException but no exception was thrown.
Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => Campaign.StartNow("SUMMER", 0.20m, TimeSpan.FromDays(120), TimeProvider.System))
90 gün kuralı hiç yazılmamış; test yıllardır yanlış sebeple yeşilmiş. [ExpectedException] method'un herhangi bir yerinden, herhangi bir sebeple fırlayan aynı tipte exception'ı kabul ediyor; arrange satırında fırlayan exception da testi geçiriyor. Assert.ThrowsExactly exception'ı yalnız verdiğin lambda'nın içinde arıyor ve sonucu döndürdüğü için mesajı, parametre adını, property'leri kontrol edebiliyorsun. Kural production'a eklendi:
public static readonly TimeSpan MaximumDuration = TimeSpan.FromDays(90);
public static Campaign StartNow(string code, decimal discountRate, TimeSpan duration, TimeProvider clock)
{
ArgumentOutOfRangeException.ThrowIfGreaterThan(discountRate, 0.5m);
ArgumentOutOfRangeException.ThrowIfGreaterThan(duration, MaximumDuration);
DateTimeOffset now = clock.GetUtcNow();
return new Campaign(code, discountRate, now, now + duration);
}
ThrowIfGreaterThan parametre adını CallerArgumentExpression ile kendisi dolduruyor; testteki "duration" beklentisi bu yüzden çalışıyor. NUnit 5 yazısındaki atlanan test gibi, bu da yükseltmenin bulduğu bir production eksiği.
MSTest.Sdk ve Microsoft.Testing.Platform
MSTest 4.5.3 11 Kasım 2026'da çıktı, lisansı MIT (kısıtlaması çok az, kısa bir açık kaynak lisansı). Test projesini kurmanın önerilen yolu MSTest.Sdk, bir MSBuild project SDK'sı:
<Project Sdk="MSTest.Sdk/4.5.3">
<PropertyGroup>
<MSTestAnalysisMode>Recommended</MSTestAnalysisMode>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Bookstore.Catalog\Bookstore.Catalog.csproj" />
</ItemGroup>
</Project>
Paket referansı yok: SDK, MSTest'in framework'ünü, adapter'ını, analyzer'larını ve Microsoft.Testing.Platform (MTP) runner'ını kendisi ekliyor, projeyi executable yapıyor. Sürüm Sdk attribute'unda; projede central package management açık olmasına rağmen ek ayar gerekmedi. Varsayılan runner MTP; <UseVSTest>true</UseVSTest> VSTest'e döndürüyor. 4.0'dan itibaren SDK, MTP modunda Microsoft.NET.Test.Sdk paketini eklemiyor; vstest.console ile çalıştırman gerekiyorsa paketi elle eklemelisin. Bir ayrıntı: .NET 10 SDK'nın (10.0.404) dotnet new mstest template'i MSTest.Sdk yerine MSTest metapaketini ve 4.0.2 sürümünü kullanıyor; template'ten başlayan projede sürümü yükselt.
MSTestAnalysisMode, analyzer'ların hangi kurallarla ve hangi önem derecesiyle çalışacağını seçiyor. Recommended, varsayılan olarak "info" seviyesindeki kuralları uyarıya çeviriyor; aşağıda örneklerini göreceğiz.
TestClass, TestMethod, DataRow ve DynamicData
Temel yapı: [TestClass] ile işaretli class, [TestMethod] ile işaretli method'lar. MSTest de xUnit gibi her test için class'ın yeni bir instance'ını oluşturuyor; NUnit'teki paylaşılan instance sorunu burada yok. Veri odaklı testler için [DataRow]:
// Attribute argümanları decimal olamaz: fiyatlar kuruş, oranlar yüzde olarak
[TestMethod]
[DataRow(1250, 10, 1125)]
[DataRow(990, 15, 842)]
[DataRow(5, 50, 3)]
public void Discount_is_rounded_away_from_zero(int priceKurus, int ratePercent, int expectedKurus)
{
Campaign campaign = Campaign.StartNow("AUTUMN26", ratePercent / 100m, TimeSpan.FromDays(7), Clock);
Assert.AreEqual(expectedKurus / 100m, campaign.Apply(priceKurus / 100m));
}
C#'ta attribute argümanı decimal olamıyor. İlk denemede parametreleri decimal yapıp [DataRow(12.50, 0.10, 11.25)] yazdım; analyzer uyardı, test de çalışmadı:
warning MSTEST0014: DataRow argument types do not match method parameter types. Parameter 'price' expects type 'decimal', but the provided value has type 'double'; ...
failed Decimal_rows (12.5,0.1,11.25) (4ms)
Cannot run test method 'Bookstore.Catalog.Pricing.Tests.Scratch21.Decimal_rows': Test data doesn't match method parameters. Either the count or types are different.
Test expected 3 parameter(s), with types 'Decimal, Decimal, Decimal',
but received 3 argument(s), with types 'Double, Double, Double'.
String alıp decimal.Parse ile çevirmek akla gelen ikinci yol, ama decimal.Parse("12.50") culture'a bağlı: Türkçe bölge ayarlı bir Windows'ta nokta binlik ayırıcı sayılır ve sonuç 1250 olur. Para tutarını kuruş olarak tam sayıyla vermek iki sorunu da ortadan kaldırıyor; tutarlar gerçekten decimal olmalıysa [DynamicData] kullan.
[DynamicData] veriyi bir method'dan, property'den ya da field'dan alıyor; 4.x'te kaynağın türünü kendisi buluyor ve ValueTuple ile tip güvenli yazılıyor:
public static IEnumerable<(DateTimeOffset At, bool Active)> BlackFridayMoments =>
[
(new DateTimeOffset(2026, 11, 26, 23, 59, 59, TimeSpan.Zero), false),
(new DateTimeOffset(2026, 11, 27, 0, 0, 0, TimeSpan.Zero), true),
(new DateTimeOffset(2026, 11, 29, 23, 59, 59, TimeSpan.Zero), true),
(new DateTimeOffset(2026, 11, 30, 0, 0, 0, TimeSpan.Zero), false),
];
[TestMethod]
[DynamicData(nameof(BlackFridayMoments))]
public void Campaign_is_active_from_start_until_just_before_the_end(DateTimeOffset at, bool active)
{
Campaign blackFriday = Campaign.StartNow("BLACKFRIDAY", 0.30m, TimeSpan.FromDays(3), Clock);
Assert.AreEqual(active, blackFriday.IsActive(at));
}
Her satıra görünen ad, kategori ya da atlama nedeni vermek gerekiyorsa kaynak TestDataRow<T> döndürüyor (xUnit'in TheoryDataRow'unun karşılığı). 4.4 ile gelen [CombinatorialData] da NUnit'in [Combinatorial]'ına benzer şekilde parametre değerlerinin bütün kombinasyonlarını üretiyor.
Assert API'si
Exception için iki method var: Assert.ThrowsExactly<T> yalnız tam olarak T tipini, Assert.Throws<T> T ve alt tiplerini kabul ediyor; async karşılıkları ThrowsExactlyAsync ve ThrowsAsync. Eski Assert.ThrowsException 4.0'da kaldırıldı. Diğer method'ların çoğu 3.8 ile 4.4 arasında eklendi ve eski projelerde Assert.IsTrue(...) ile yazılmış kontrollerin yerini alıyor:
// Static ve TestContext alıyor: bu class'ın ilk testinden önce bir kez çalışır
[ClassInitialize]
public static void LoadCalendar(TestContext context)
{
FixedClock clock = new(new DateTimeOffset(2026, 1, 1, 0, 0, 0, TimeSpan.Zero));
calendar =
[
Campaign.StartNow("WINTER26", 0.20m, TimeSpan.FromDays(60), clock),
Campaign.StartNow("READING-WEEK", 0.10m, TimeSpan.FromDays(7), clock),
Campaign.StartNow("BOOKFAIR", 0.25m, TimeSpan.FromDays(10), clock),
];
context.WriteLine($"Calendar loaded with {calendar.Count} campaigns");
}
[TestMethod]
public void Calendar_codes_follow_the_naming_rule()
{
Assert.HasCount(3, calendar);
Assert.ContainsSingle(c => c.Code == "BOOKFAIR", calendar);
foreach (Campaign campaign in calendar)
{
Assert.MatchesRegex("^[A-Z0-9-]+$", campaign.Code);
}
}
[TestMethod]
public void Discounts_stay_within_the_allowed_range()
{
foreach (Campaign campaign in calendar)
{
Assert.IsInRange(0.05m, 0.50m, campaign.DiscountRate);
}
}
Parametre sıralarına dikkat: MSTest'te beklenen değer önce (AreEqual(expected, actual), HasCount(3, calendar)), predicate koleksiyondan önce (ContainsSingle(predicate, collection), xUnit'in Assert.Single(collection, predicate)'inin tersi), aralık değerden önce (IsInRange(min, max, value)), pattern değerden önce (MatchesRegex(pattern, value)). ContainsSingle'ı xUnit alışkanlığıyla (calendar, c => ...) diye yazınca derleme CS1503 ile durdu.
4.x'in hata mesajları, Cevap bölümünde gördüğün gibi, assertion'ın kaynak kodunu (CallerArgumentExpression) ve string'lerin ilk farkının yerini gösteriyor. CollectionAssert ve StringAssert hâlâ duruyor, ama dokümana göre CollectionAssert ileride deprecated olabilir; yeni kodda Assert üzerindeki karşılıklarını kullan. Birden fazla hatayı tek raporda toplamak için Assert.Scope() var, ama deneysel bir API: kullanınca MSTESTEXP diagnostic'i geliyor ve şekli değişebiliyor.
Yaşam Döngüsü ve TestContext
[AssemblyInitialize]/[AssemblyCleanup] assembly başına, [ClassInitialize]/[ClassCleanup] class başına, [TestInitialize]/[TestCleanup] (ya da constructor ve Dispose) test başına bir kez çalışıyor. Initialize method'ları public static olmalı ve tek bir TestContext parametresi almalı. İmza yanlış olunca 4.x'te sonuç beklenenden ağır. ClassInitialize'ı instance method'u olarak, parametresiz yazdım:
Zero tests ran
Error output: Unhandled exception. Microsoft.VisualStudio.TestTools.UnitTesting.MSTestException: Discovery failed for source 'bin/Debug/net10.0/Bookstore.Catalog.Pricing.Tests.dll' with 1 errors:
MSTestAdapter failed to discover tests in class 'Bookstore.Catalog.Pricing.Tests.Scratch21' of assembly 'bin/Debug/net10.0/Bookstore.Catalog.Pricing.Tests.dll' because Method Bookstore.Catalog.Pricing.Tests.Scratch21.LoadCalendar has wrong signature. The method must be static, public, does not return a value and should take a single parameter of type TestContext. Additionally, if you are using async-await in method then return-type must be 'Task' or 'ValueTask'..
Test run summary: Zero tests ran
Tek bir class'taki hata, projedeki bütün testlerin çalışmasını engelledi. 4.0'da TreatDiscoveryWarningsAsErrors'ın varsayılanı true oldu; 3.x'te aynı hata bir uyarıyla geçiştirilebiliyordu. Derlemede MSTEST0010 uyarısı ("ClassInitialize method 'LoadCalendar' signature is invalid") bunu zaten söylüyordu.
TestContext test hakkında bilgi veriyor. 3.6'dan beri constructor'dan alınabiliyor; 3.11'den beri CancellationToken property'si var, test zaman aşımına uğradığında ya da koşu iptal edildiğinde iptal ediliyor:
[TestClass]
public sealed class CampaignCalendarTests(TestContext testContext)
{
...
[TestMethod]
public async Task Test_context_carries_the_name_and_the_cancellation_token()
{
await Task.Delay(10, testContext.CancellationToken);
Assert.AreEqual(nameof(Test_context_carries_the_name_and_the_cancellation_token), testContext.TestName);
Assert.StartsWith("Bookstore.Catalog.Pricing", testContext.FullyQualifiedTestClassName!);
}
}
4.x'te TestContext yanlış aşamada kullanılınca exception fırlatıyor: örneğin AssemblyInitialize içinde TestName ya da FullyQualifiedTestClassName okumak, ClassInitialize içinde TestName okumak artık hata.
3.x'ten 4.x'e: Değişenler, Analyzer'lar ve Paralellik
Microsoft Learn'deki "Migrate from MSTest v3 to v4" sayfası değişiklikleri listeliyor; bir projeyi yükseltirken en çok karşılaşacakların:
<ul>
<li><code>[ExpectedException]</code> ve <code>Assert.ThrowsException</code> kaldırıldı; yerlerine <code>Assert.ThrowsExactly</code>/<code>Throws</code> (MSTEST0006 ve MSTEST0039 code fix'leri 3.x'te dönüşümü yapıyor).</li>
<li>Hem <code>message</code> hem <code>params object[]</code> alan assert overload'ları kaldırıldı; mesaj string interpolation ile yazılıyor.</li>
<li><code>ClassCleanupBehavior</code> kaldırıldı: <code>ClassCleanup</code> artık her zaman class'ın sonunda çalışıyor. <code>TestContext.Properties</code> tip güvenli bir <code>IDictionary<string, object></code> oldu, <code>TestTimeout</code> enum'u kaldırıldı.</li>
<li>Davranış değişiklikleri: keşif uyarıları varsayılan olarak hata, MTP altında AppDomain izolasyonu varsayılan olarak kapalı, test ID'lerinin üretimi değişti (Azure DevOps'ta test geçmişi bu yüzden kopabiliyor).</li>
<li>.NET Core 3.1 ile .NET 7 arası hedefler bırakıldı; en düşük .NET 8 ve .NET Framework 4.6.2. 3.x ile derlenmiş kütüphaneler 4.x'e karşı yeniden derlenmeli.</li>
</ul>
Analyzer'lar MSTest'in en güçlü tarafı. Recommended modunda, bilerek hatalı yazılmış bir test dosyası:
warning MSTEST0014: DataRow argument types do not match method parameter types. Parameter 'price' expects type 'decimal', but the provided value has type 'double'; ...
warning MSTEST0017: Assertion arguments should be passed in the correct order. 'actual' and 'expected'/'notExpected' arguments have been swapped. (https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0017)
warning MSTEST0037: Use 'Assert.AreEqual' instead of 'Assert.IsTrue' (https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0037)
warning MSTEST0049: Consider using the overload that accepts a CancellationToken and pass 'TestContext.CancellationToken' (https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0049)
MSTEST0017 Assert.AreEqual(campaign.Apply(12.50m), 11.25m) satırında sabit değerin actual yerinde durduğunu fark etti; NUnit yazısının son tuzağının MSTest'teki derleme zamanı karşılığı. MSTEST0037 ve MSTEST0049 varsayılan modda "info" seviyesinde kaldığı için Recommended olmadan derleme çıktısında görünmedi.
MSTest varsayılan olarak testleri sırayla çalıştırıyor. Paralelliği assembly seviyesinde açtık:
// Test method'ları paralel çalışabilir, class'lar arasında ve içinde
[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)]
ExecutionScope.ClassLevel yalnız class'ları paralel çalıştırıyor; Workers (varsayılan 0, yani işlemci sayısı) eşzamanlı test sayısını sınırlıyor. Paylaşılan bir kaynağa dokunan class ya da method [DoNotParallelize] ile dışarıda kalıyor. MSTEST0001 (info) assembly'nin paralellik niyetini [Parallelize] ya da [DoNotParallelize] ile açıkça yazmasını öneriyor; kurumsal projelerde sık görülen "testler neden yavaş" sorusunun cevabı çoğu zaman bu tek satır.
TUnit'e Kısa Bakış ve "Hangi Framework?"
TUnit en genç .NET test framework'ü: testleri reflection yerine derleme anında bir source generator ile keşfediyor, yalnız MTP üzerinde çalışıyor (VSTest desteği yok) ve assertion'ları await edilerek yazılıyor. Yazım günü son sürüm 1.85.0 (14 Kasım 2026), lisansı MIT. Catalog'daki ISBN (International Standard Book Number; kitabın 13 haneli uluslararası kimlik numarası) kuralı için küçük bir proje:
public sealed class Isbn13Tests
{
[Test]
[Arguments("9780441172719", true)]
[Arguments("9780441172718", false)]
[Arguments("978044117271", false)]
[Arguments("1234567890123", false)]
public async Task Isbn_is_validated(string isbn, bool expected)
{
await Assert.That(Isbn13.IsValid(isbn)).IsEqualTo(expected);
}
[Test]
public async Task Prefix_must_have_twelve_digits()
{
await Assert.That(() => Isbn13.CheckDigit("978044117")).ThrowsExactly<ArgumentException>();
}
}
Kontrol hanesini bilerek yanlış bekleyen bir testin mesajı:
failed Wrong (18ms)
[Test Failure] AssertionException: Expected to be 8
but found 9
TUnit'in test projesi sıradan bir Microsoft.NET.Sdk projesi ve tek paket referansı TUnit; global.json'daki MTP ayarıyla solution'daki diğer projelerle aynı dotnet test komutunda çalıştı. Varsayılan ayarlarıyla TestResults klasörüne bir HTML raporu da yazıyor. Async assertion'lar NUnit 5'teki tuzağın TUnit'teki karşılığını getiriyor: await'i unutulan bir Assert.That(...) hiçbir şey doğrulamaz. TUnit bunu baştan engelliyor; await'siz bir assertion derlenmedi: error TUnitAssertions0002: Assert statements must be awaited - all TUnit assertions return Task.
Dört framework'te aynı iş:
| İş | xUnit v3 | NUnit 5 | MSTest 4 | TUnit 1.85 |
|---|---|---|---|---|
| Test | [Fact] | [Test] | [TestMethod] | [Test] |
| Satır içi veri | [InlineData] | [TestCase] | [DataRow] | [Arguments] |
| Eşitlik | Assert.Equal(e, a) | Assert.That(a, Is.EqualTo(e)) | Assert.AreEqual(e, a) | await Assert.That(a).IsEqualTo(e) |
| Tam exception tipi | Assert.Throws<T> | Throws.TypeOf<T>() | Assert.ThrowsExactly<T> | ThrowsExactly<T>() |
| Exception ya da alt tipi | Assert.ThrowsAny<T> | Throws.InstanceOf<T>() | Assert.Throws<T> | Throws<T>() |
| Her testten önce | constructor | [SetUp] | [TestInitialize] / constructor | [Before(Test)] |
| Class başına bir kez | IClassFixture<T> | [OneTimeSetUp] | [ClassInitialize] (static) | [Before(Class)] (static) |
Assert.Throws<T>'nin anlamına dikkat: xUnit'te tam tip, MSTest'te tip ve alt tipleri. İki framework arasında test taşırken en kolay gözden kaçan fark bu.
Hangi framework? Mevcut bir projede, kullandığı framework'te kal: bu blogdaki NUnit ve MSTest yükseltmelerinin ikisi de, değiştirmekten çok güncel tutmanın değerini gösterdi. Yeni bir projede seçimi ekibin alışkanlığı ve araç zinciri belirlesin; dördü de MTP üzerinde, aynı dotnet test komutuyla ve aynı CI adımıyla çalışıyor. xUnit .NET ekosisteminde en yaygın olanlardan biri ve bu serinin seçimi; MSTest, analyzer'ları ve Visual Studio entegrasyonuyla Microsoft ağırlıklı ekiplerde; NUnit, constraint model'i ve zengin attribute seti ile uzun yaşamış kurumsal kodda güçlü. TUnit'i yeni bir projede, Native AOT (uygulama çalışmadan önce makine koduna derleme) ya da çok büyük test suite'lerinde keşif süresi önemliyse değerlendir; genç bir proje olduğu için sürümler sık geliyor (1.84.0 ile 1.85.0 arasında iki gün var) ve bu, sürüm sabitlemeyi daha da önemli kılıyor.
Üretimde Dikkat
Yükseltmeyi 3.x'in son sürümünde başlat. 3.11.1'de MSTEST0006, MSTEST0039 ve diğer uyarıları temizlemek, 4.x'e geçişi derleme hatası listesinden bir sürüm numarası değişikliğine indiriyor. Önce Tahmin Et'teki gibi, mekanik dönüşümden sonra her exception testine "neden" sorusunu sor: parametre adı, mesaj ya da bir property.
MSTestAnalysisMode'u Recommended yap ve uyarıları CI'da hataya çevir. MSTEST0017 (ters argüman) ve MSTEST0049 (iptal edilmeyen async çağrı) gibi kurallar, kod incelemesinde gözden kaçan hataları derlemede yakalıyor.
Azure DevOps'ta test geçmişini izliyorsan 4.0'daki test ID değişikliğini bil: yükseltmeden sonra testler geçmişte yeni testler gibi görünebilir.
Sık Düşülen Tuzaklar
v2'den kalma [ExpectedException]. 4.x'te derlenmiyor, ama asıl sorun 3.x'te derlenirken yaptığı: method'un herhangi bir satırındaki aynı tipte exception'ı başarı sayıyor. Önce Tahmin Et'teki test, var olmayan bir kuralı yıllarca "doğruladı".
ClassInitialize'ın imzası. public static, tek TestContext parametresi, dönüş tipi void, Task ya da ValueTask. Yanlış imza 4.x'te bütün assembly'nin keşfini durduruyor; MSTEST0010 uyarısını görmezden gelme.
DataRow'da decimal ve culture. Attribute argümanı decimal olamıyor; string'den decimal.Parse ile çevirmek bölge ayarına bağlı. Tam sayı (kuruş) ya da DynamicData kullan.
Assert.Throws<T>'yi xUnit'teki anlamıyla kullanmak. MSTest'te alt tipleri de kabul ediyor; tam tip için ThrowsExactly<T>.
Küçük Bir Deneme
★☆☆ Campaign_longer_than_90_days_is_rejected'ı [DataRow] ile üç süreye genişlet: 90 gün (geçerli), 90 gün 1 saniye ve 365 gün. Geçerli süreyi aynı test içinde mi, ayrı bir testte mi doğrulardın?
★★☆ BlackFridayMoments'ı TestDataRow<(DateTimeOffset, bool)> döndürecek şekilde yeniden yaz ve her satıra bir DisplayName ver. Test sonuçlarında ne değişti?
★★★ CampaignTests'i xUnit, NUnit ve TUnit'e taşı (yukarıdaki tabloyla). Hangi assertion'ın anlamı framework'e göre değişti, hangi testte tablo yetmedi?
Bu yazıyla Unit Test serisi tamamlanıyor. İlk yazıda sorduğumuz soru, bir "unit"in ne olduğu, kavram yazılarında cevabını buldu: davranışı test eden, refactoring'e dayanan, hızlı ve okunur testler; mock'lar yalnız sistem sınırında; tasarım testin işini kolaylaştıracak biçimde. Son sekiz yazı aynı fikirleri her gün kullanılan araçlarla somutlaştırdı: xUnit'in Assert API'si, AwesomeAssertions ve Shouldly, NSubstitute ve Moq, HttpClient, Bogus ve AutoFixture, NUnit ve MSTest. Bu yazılardan geriye kalması gereken araç adlarından çok bir alışkanlık: her testin neyi, neden doğruladığını sormak. Testi koddan önce yazmak TDD (Test-Driven Development; önce testi, sonra kodu yazma yaklaşımı) serisinin, gerçek veritabanı ve HTTP ile çalışan testler Integration ve E2E (uçtan uca) Test serisinin konusu; test verisini hangi değerlerden seçeceğin ise ilerideki Test Tasarım Teknikleri serisinde.