Birim testi için uygulamanızın parçalarını birbirinden yalıtmak üzere saplamalar kullanma
Saplama türleri , Microsoft Fakes çerçevesi tarafından sağlanan ve test ettiğiniz bileşenin bağlı olduğu diğer bileşenlerden kolayca yalıtılmasını sağlayan önemli bir teknolojidir. Saplama, test sırasında başka bir bileşenin yerini alacak küçük bir kod parçası işlevi görür. Saptamaları kullanmanın temel avantajlarından biri, test yazmayı kolaylaştırmak için tutarlı sonuçlar elde etmektir. Diğer bileşenler henüz tam olarak işlevsel olmasa bile saptamaları kullanarak testleri yürütebilirsiniz.
Saplamaları etkili bir şekilde uygulamak için, bileşeninizi uygulamanın diğer bölümlerindeki somut sınıflar yerine birincil olarak arabirimlere bağlı olacak şekilde tasarlamanız önerilir. Bu tasarım yaklaşımı, ayrıştırma işlemini teşvik eder ve bir bölümde değişiklik yapılması için başka bir bölümde değişiklik yapılması olasılığını azaltır. Test söz konusu olduğunda, bu tasarım deseni gerçek bir bileşen için saplama uygulamasının değiştirilip etkin yalıtımın ve hedef bileşenin doğru test edilmesine olanak tanır.
Örneğin, ilgili bileşenleri gösteren diyagramı ele alalım:
Bu diyagramda, test altındaki bileşen genellikle StockAnalyzer
adlı RealStockFeed
başka bir bileşene dayanır. Ancak, RealStockFeed
yöntemleri her çağrıldığında farklı sonuçlar döndürdüğünden test için bir zorluk oluşturur. Bu değişkenlik tutarlı ve güvenilir bir şekilde test StockAnalyzer
edilmesini zorlaştırıyor.
Test sırasında bu engeli aşmak için bağımlılık ekleme uygulamasını benimseyebiliriz. Bu yaklaşım, kodunuzu uygulamanızın başka bir bileşenindeki sınıflardan açıkça bahsetmeyen bir şekilde yazmayı içerir. Bunun yerine, diğer bileşenin ve saplamanın test amacıyla uygulayabileceği bir arabirim tanımlarsınız.
Kodunuzda bağımlılık ekleme özelliğini nasıl kullanabileceğinize yönelik bir örnek aşağıda verilmişti:
Saptama sınırlamaları
Saptamalar için aşağıdaki sınırlamaları gözden geçirin.
İşaretçileri olan yöntem imzaları desteklenmez.
Saplama türleri sanal yöntem gönderimi kullandığından, korumalı sınıflar veya statik yöntemler saplama türleri kullanılarak saplanamaz. Bu gibi durumlarda, birim testi için uygulamanızı diğer derlemelerden yalıtmak için dolguları kullanma başlığında açıklandığı gibi dolgu türlerini kullanın
Saplama Oluşturma: Adım Adım Kılavuz
Bu alıştırmaya motive edici bir örnekle başlayalım: önceki diyagramda gösterilen alıştırma.
Sınıf Kitaplığı Oluşturma
Sınıf kitaplığı oluşturmak için bu adımları izleyin.
Visual Studio'yu açın ve bir Sınıf Kitaplığı projesi oluşturun.
Proje özniteliklerini yapılandırın:
- Proje adını StockAnalysis olarak ayarlayın.
- Çözüm adını StubsTutorial olarak ayarlayın.
- Proje Hedef çerçevesini .NET 8.0 olarak ayarlayın.
Varsayılan Class1.cs dosyasını silin.
IStockFeed.cs adlı yeni bir dosya ekleyin ve aşağıdaki arabirim tanımına kopyalayın:
StockAnalyzer.cs adlı başka bir yeni dosya ekleyin ve aşağıdaki sınıf tanımına kopyalayın:
Test Projesi Oluşturma
Alıştırma için test projesini oluşturun.
Çözüme sağ tıklayın ve MSTest Test Projesi adlı yeni bir proje ekleyin.
Proje adını TestProjesi olarak ayarlayın.
Projenin hedef çerçevesini .NET 8.0 olarak ayarlayın.
Fakes derlemesi ekleme
Proje için Fakes derlemesini ekleyin.
öğesine
StockAnalyzer
bir proje başvurusu ekleyin.Fakes Derlemesini ekleyin.
Çözüm Gezgini'da derleme başvuruyu bulun:
Eski bir .NET Framework Projesi (SDK olmayan stil) için birim testi projenizin Başvurular düğümünü genişletin.
.NET Framework, .NET Core veya .NET 5.0 veya üzerini hedefleyen SDK stilinde bir proje için, Derlemeler, Projeler veya Paketler altında sahtesini yapmak istediğiniz derlemeyi bulmak için Bağımlılıklar düğümünü genişletin.
Visual Basic'te çalışıyorsanız Başvurular düğümünü görmek için Çözüm Gezgini araç çubuğunda Tüm Dosyaları Göster'i seçin.
Saplama oluşturmak istediğiniz sınıf tanımlarını içeren derlemeyi seçin.
Kısayol menüsünde Sahte Derleme Ekle'yi seçin.
Birim testi oluşturma
Şimdi birim testini oluşturun.
Aşağıdaki
Test Method
tanımı eklemek için varsayılan UnitTest1.cs dosyasını değiştirin.[TestClass] class UnitTest1 { [TestMethod] public void TestContosoPrice() { // Arrange: int priceToReturn = 345; string companyCodeUsed = ""; var componentUnderTest = new StockAnalyzer(new StockAnalysis.Fakes.StubIStockFeed() { GetSharePriceString = (company) => { // Store the parameter value: companyCodeUsed = company; // Return the value prescribed by this test: return priceToReturn; } }); // Act: int actualResult = componentUnderTest.GetContosoPrice(); // Assert: // Verify the correct result in the usual way: Assert.AreEqual(priceToReturn, actualResult); // Verify that the component made the correct call: Assert.AreEqual("COOO", companyCodeUsed); } }
Buradaki özel sihir parçası sınıfımız
StubIStockFeed
. Başvurulan derlemedeki her arabirim için saptama sınıfı Microsoft Fakes mekanizması oluşturur. Saplama sınıfının adı arabirimin adından türetilir ve ön ek olarak "Fakes.Stub
" ve parametre türü adları eklenir.Saptamalar ayrıca olaylar ve genel yöntemlerle ilgili olarak özellik okuyucu ve ayarlayıcılar için oluşturulur. Daha fazla bilgi için bkz . Birim testi için uygulamanızın parçalarını birbirinden yalıtmak için saplamaları kullanma.
Test Gezgini'ni açın ve testi çalıştırın.
Tür üyelerinin farklı türleri için saptamalar
Farklı tür üyeleri için saplamalar vardır.
Yöntemler
Sağlanan örnekte, saplama sınıfının bir örneğine temsilci eklenerek yöntemler saplanabilir. Saptama türünün adı yöntemi ve parametreleri adlarından türetilir. Örneğin, aşağıdaki IStockFeed
arabirimi ve yöntemini GetSharePrice
göz önünde bulundurun:
// IStockFeed.cs
interface IStockFeed
{
int GetSharePrice(string company);
}
kullanarak GetSharePriceString
bir GetSharePrice
saplama ekleriz:
// unit test code
var componentUnderTest = new StockAnalyzer(new StockAnalysis.Fakes.StubIStockFeed()
{
GetSharePriceString = (company) =>
{
// Store the parameter value:
companyCodeUsed = company;
// Return the value prescribed by this test:
return priceToReturn;
}
});
Bir yöntem için saplama sağlamazsanız Fakes, dönüş türünün sonucunu döndüren default value
bir işlev oluşturur. Sayılar için varsayılan değer 0'dır. Sınıf türleri için varsayılan değer C# veya Nothing
Visual Basic'tirnull
.
Özellikler
Özellik alıcıları ve ayarlayıcıları ayrı temsilciler olarak sunulur ve ayrı ayrı saplanabilir. Örneğin, özelliğini IStockFeedWithProperty
göz önünde bulundurunValue
:
interface IStockFeedWithProperty
{
int Value { get; set; }
}
Bir otomatik özelliğin alıcısını ve ayarlayıcısını Value
saplayıp benzetimini yapmak için aşağıdaki kodu kullanabilirsiniz:
// unit test code
int i = 5;
var stub = new StubIStockFeedWithProperty();
stub.ValueGet = () => i;
stub.ValueSet = (value) => i = value;
Bir özelliğin ayarlayıcısı veya alıcısı için saptama yöntemleri sağlamazsanız Fakes değerleri depolayan bir saptama oluşturur ve saplama özelliğinin basit bir değişken gibi davranmasını sağlar.
Olaylar
Olaylar temsilci alanları olarak sunulur ve saplanan olayların yalnızca olay yedekleme alanı çağrılarak tetiklenmesine olanak sağlar. Saptamak için aşağıdaki arabirimi ele alalım:
interface IStockFeedWithEvents
{
event EventHandler Changed;
}
Olayı başlatmak Changed
için, yedekleme temsilcisini çağırırsınız:
// unit test code
var withEvents = new StubIStockFeedWithEvents();
// raising Changed
withEvents.ChangedEvent(withEvents, EventArgs.Empty);
Genel yöntemler
Yöntemin istenen her örneği için bir temsilci sağlayarak genel yöntemleri saplayabilirsiniz. Örneğin, genel bir yöntemle aşağıdaki arabirim göz önünde bulundurulduğunda:
interface IGenericMethod
{
T GetValue<T>();
}
Örneklemeyi GetValue<int>
aşağıdaki gibi saplayabilirsiniz:
[TestMethod]
public void TestGetValue()
{
var stub = new StubIGenericMethod();
stub.GetValueOf1<int>(() => 5);
IGenericMethod target = stub;
Assert.AreEqual(5, target.GetValue<int>());
}
Kod başka bir örneklemeyle çağırırsa GetValue<T>
, saplama davranışı yürütür.
Sanal sınıf saptamaları
Önceki örneklerde saptamalar arabirimlerden üretilmedi. Ancak, sanal veya soyut üyeleri olan bir sınıftan saplamalar da oluşturabilirsiniz. Örneğin:
// Base class in application under test
public abstract class MyClass
{
public abstract void DoAbstract(string x);
public virtual int DoVirtual(int n)
{
return n + 42;
}
public int DoConcrete()
{
return 1;
}
}
Bu sınıftan oluşturulan saplamada ve DoVirtual()
için DoAbstract()
temsilci yöntemleri ayarlayabilirsiniz, ancak ayarlayamayabilirsinizDoConcrete()
.
// unit test
var stub = new Fakes.MyClass();
stub.DoAbstractString = (x) => { Assert.IsTrue(x>0); };
stub.DoVirtualInt32 = (n) => 10 ;
Sanal yöntem için temsilci sağlamazsanız Fakes varsayılan davranışı sağlayabilir veya temel sınıfta yöntemini çağırabilir. Base yönteminin çağrılmış olması için özelliğini ayarlayın CallBase
:
// unit test code
var stub = new Fakes.MyClass();
stub.CallBase = false;
// No delegate set - default delegate:
Assert.AreEqual(0, stub.DoVirtual(1));
stub.CallBase = true;
// No delegate set - calls the base:
Assert.AreEqual(43,stub.DoVirtual(1));
Saptamaların varsayılan davranışını değiştirme
Oluşturulan her saplama türü, özelliği aracılığıyla arabirimin IStubBehavior
bir örneğini IStub.InstanceBehavior
tutar. Bu davranış, bir istemci ekli özel temsilcisi olmayan bir üyeyi her çağırışında çağrılır. Davranış ayarlı değilse, özelliği tarafından StubsBehaviors.Current
döndürülen örneği kullanır. Varsayılan olarak, bu özellik özel durum oluşturan bir NotImplementedException
davranış döndürür.
Herhangi bir saplama örneğinde özelliğini ayarlayarak InstanceBehavior
istediğiniz zaman davranışı değiştirebilirsiniz. Örneğin, aşağıdaki kod parçacığı davranışı değiştirerek saplama hiçbir şey yapmaz veya dönüş türünün default(T)
varsayılan değerini döndürür:
// unit test code
var stub = new StockAnalysis.Fakes.StubIStockFeed();
// return default(T) or do nothing
stub.InstanceBehavior = StubsBehaviors.DefaultValue;
Davranış, davranışın özelliğiyle StubsBehaviors.Current
ayarlandığı tüm saptama nesneleri için de genel olarak değiştirilebilir:
// Change default behavior for all stub instances where the behavior has not been set.
StubBehaviors.Current = BehavedBehaviors.DefaultValue;