Birim testi için uygulamanızı yalıtmak için dolguları kullanma
Microsoft Fakes Framework tarafından kullanılan iki önemli teknolojiden biri olan dolgu türleri, test sırasında uygulamanızın bileşenlerini yalıtmada etkili olur. Çağrıları keserek ve belirli yöntemlere yönlendirerek çalışırlar ve bunu testinizde özel koda yönlendirebilirsiniz. Bu özellik, dış koşullardan bağımsız olarak her çağrı sırasında sonuçların tutarlı ve tahmin edilebilir olmasını sağlayarak bu yöntemlerin sonucunu yönetmenizi sağlar. Bu denetim düzeyi test sürecini kolaylaştırır ve daha güvenilir ve doğru sonuçlar elde edilmesine yardımcı olur.
Kodunuz ve çözümünüzün bir parçasını oluşturmayan derlemeler arasında bir sınır oluşturmanız gerektiğinde dolguları kullanın. Amacınız çözümünüzün bileşenlerini birbirinden yalıtmak olduğunda saplamaların kullanılması önerilir.
(Saplamalar için daha ayrıntılı bir açıklama için bkz. Birim testi için uygulamanızın parçalarını birbirinden yalıtmak için saplamaları kullanın.)
Dolgu sınırlamaları
Dolguların sınırlamaları olduğuna dikkat etmek önemlidir.
Dolgular .NET temel sınıfındaki belirli kitaplıklardan, özellikle .NET Framework'teki mscorlib ve System'den ve .NET Core veya .NET 5+ içindeki System.Runtime'dan tüm türlerde kullanılamaz. Başarılı ve etkili bir test stratejisi sağlamak için test planlama ve tasarım aşamasında bu kısıtlama dikkate alınmalıdır.
Dolgu Oluşturma: Adım Adım Kılavuz
Bileşeninizin çağrısı içerdiğini System.IO.File.ReadAllLines
varsayalım:
// Code under test:
this.Records = System.IO.File.ReadAllLines(path);
Sınıf Kitaplığı Oluşturma
Visual Studio'yu açma ve proje oluşturma
Class Library
Proje adını ayarlama
HexFileReader
Çözüm adını
ShimsTutorial
ayarlayın.Projenin hedef çerçevesini .NET Framework 4.8 olarak ayarlayın
Varsayılan dosyayı silme
Class1.cs
Yeni bir dosya
HexFile.cs
ekleyin ve aşağıdaki sınıf tanımını ekleyin:
Test Projesi Oluşturma
Çözüme sağ tıklayın ve yeni bir proje ekleyin
MSTest Test Project
Proje adını ayarlama
TestProject
Projenin hedef çerçevesini .NET Framework 4.8 olarak ayarlayın
Fakes Derlemesi Ekle
'a proje başvurusu ekleme
HexFileReader
Fakes Derlemesi Ekle
Çözüm Gezgini'da,
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+ 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.
tanımını
System.IO.File.ReadAllLines
içeren derlemeyiSystem
seçin.Kısayol menüsünde Sahte Derleme Ekle'yi seçin.
Tüm türler dolgularla kullanılamadığından, derleme bazı uyarı ve hatalarla sonuçlandığından, içeriğini Fakes\mscorlib.fakes
dışlamak için değiştirmeniz gerekir.
<Fakes xmlns="http://schemas.microsoft.com/fakes/2011/" Diagnostic="true">
<Assembly Name="mscorlib" Version="4.0.0.0"/>
<StubGeneration>
<Clear/>
</StubGeneration>
<ShimGeneration>
<Clear/>
<Add FullName="System.IO.File"/>
<Remove FullName="System.IO.FileStreamAsyncResult"/>
<Remove FullName="System.IO.FileSystemEnumerableFactory"/>
<Remove FullName="System.IO.FileInfoResultHandler"/>
<Remove FullName="System.IO.FileSystemInfoResultHandler"/>
<Remove FullName="System.IO.FileStream+FileStreamReadWriteTask"/>
<Remove FullName="System.IO.FileSystemEnumerableIterator"/>
</ShimGeneration>
</Fakes>
Birim testi oluşturma
Aşağıdakini eklemek için varsayılan dosyayı
UnitTest1.cs
değiştirinTestMethod
[TestMethod] public void TestFileReadAllLine() { using (ShimsContext.Create()) { // Arrange System.IO.Fakes.ShimFile.ReadAllLinesString = (s) => new string[] { "Hello", "World", "Shims" }; // Act var target = new HexFile("this_file_doesnt_exist.txt"); Assert.AreEqual(3, target.Records.Length); } }
Tüm dosyaları gösteren Çözüm Gezgini aşağıdadır
Test Gezgini'ni açın ve testi çalıştırın.
Her dolgu bağlamı düzgün bir şekilde atılması kritik önem taşır. Bir kural olarak, kayıtlı dolguların düzgün temizlenmesini sağlamak için deyiminin using
içini çağırınShimsContext.Create
. Örneğin, yöntemini her zaman Ocak 2000'in ilkini DateTime.Now
döndüren bir temsilciyle değiştiren bir test yöntemi için dolgu kaydedebilirsiniz. Test yönteminde kayıtlı dolguyu temizlemeyi unutursanız, test çalıştırmasının geri kalanı her zaman ilk Ocak 2000 DateTime.Now
değerini döndürür. Bu şaşırtıcı ve kafa karıştırıcı olabilir.
Dolgu Sınıfları için Adlandırma Kuralları
Dolgu sınıf adları, özgün tür adına ön ek eklenerek Fakes.Shim
oluşturulur. Parametre adları yöntem adına eklenir. (System.Fakes'e herhangi bir derleme başvurusu eklemeniz gerekmez.)
System.IO.File.ReadAllLines(path);
System.IO.Fakes.ShimFile.ReadAllLinesString = (path) => new string[] { "Hello", "World", "Shims" };
Dolguların Nasıl Çalıştığını Anlama
Dolgular, test edilen uygulamanın kod tabanına detours ekleyerek çalışır. Özgün yönteme bir çağrı olduğunda Fakes sistemi bu çağrıyı yeniden yönlendirmeye müdahale eder ve özel dolgu kodunuzun özgün yöntem yerine yürütülmesine neden olur.
Bu sapmaların çalışma zamanında dinamik olarak oluşturulduğunu ve kaldırıldığını unutmayın. Detours her zaman bir ShimsContext
yaşam süresi içinde oluşturulmalıdır. ShimsContext atıldığında, içinde oluşturulan tüm etkin dolgular da kaldırılır. Bunu verimli bir şekilde yönetmek için, bir using
deyim içinde detours oluşturulmasını kapsüllemenizi öneririz.
Farklı yöntemler için dolgular
Dolgular çeşitli yöntem türlerini destekler.
Statik yöntemler
Statik yöntemlerin dolgusu oluşturulurken dolguları tutan özellikler dolgu türü içinde barındırılır. Bu özellikler yalnızca hedeflenen yönteme temsilci eklemek için kullanılan bir ayarlayıcıya sahiptir. Örneğin, statik yöntemiyle MyMethod
adlı MyClass
bir sınıfımız varsa:
//code under test
public static class MyClass {
public static int MyMethod() {
...
}
}
Sürekli 5 döndürecek şekilde bir dolgu MyMethod
ekleyebiliriz:
// unit test code
ShimMyClass.MyMethod = () => 5;
Örnek yöntemleri (tüm örnekler için)
Statik yöntemler gibi örnek yöntemleri de tüm örnekler için dolgulanabilir. Bu dolguları tutan özellikler, karışıklığı önlemek için AllInstances adlı iç içe yerleştirilmiş bir türe yerleştirilir. Örnek yöntemine MyMethod
sahip bir sınıfımız MyClass
varsa:
// code under test
public class MyClass {
public int MyMethod() {
...
}
}
Örneğinden bağımsız olarak tutarlı olarak 5 döndürmesi için MyMethod
öğesine dolgu ekleyebiliriz:
// unit test code
ShimMyClass.AllInstances.MyMethod = () => 5;
oluşturulan tür yapısı ShimMyClass
aşağıdaki gibi görünür:
// Fakes generated code
public class ShimMyClass : ShimBase<MyClass> {
public static class AllInstances {
public static Func<MyClass, int>MyMethod {
set {
...
}
}
}
}
Bu senaryoda Fakes, çalışma zamanı örneğini temsilcinin ilk bağımsız değişkeni olarak geçirir.
Örnek yöntemleri (Tek Çalışma Zamanı Örneği)
Örnek yöntemleri, çağrının alıcıya bağlı olarak farklı temsilciler kullanılarak da kullanılabilir. Bu, aynı örnek yönteminin türün örneği başına farklı davranışlar sergilemesini sağlar. Bu dolguları tutan özellikler, dolgu türünün kendisinin örnek yöntemleridir. Örneklenen her dolgu türü, dolgulu bir türün ham örneğine bağlanır.
Örneğin, örnek yöntemine MyMethod
sahip bir sınıf MyClass
verilmiştir:
// code under test
public class MyClass {
public int MyMethod() {
...
}
}
İlkinin tutarlı olarak 5, ikincisinin tutarlı olarak 10 döndürmesi için MyMethod
iki dolgu türü oluşturabiliriz:
// unit test code
var myClass1 = new ShimMyClass()
{
MyMethod = () => 5
};
var myClass2 = new ShimMyClass { MyMethod = () => 10 };
oluşturulan tür yapısı ShimMyClass
aşağıdaki gibi görünür:
// Fakes generated code
public class ShimMyClass : ShimBase<MyClass> {
public Func<int> MyMethod {
set {
...
}
}
public MyClass Instance {
get {
...
}
}
}
Gerçek dolgulu tür örneğine Instance özelliği aracılığıyla erişilebilir:
// unit test code
var shim = new ShimMyClass();
var instance = shim.Instance;
Dolgu türü ayrıca dolgu türüne örtük bir dönüştürme de içerir ve dolgu türünü doğrudan kullanmanıza olanak sağlar:
// unit test code
var shim = new ShimMyClass();
MyClass instance = shim; // implicit cast retrieves the runtime instance
Oluşturucular
Oluşturucular, dolgulama için özel durum değildir; bunlar da gelecekte oluşturulacak nesnelere dolgu türleri eklemek için dolgulanabilir. Örneğin, her oluşturucu dolgu türü içinde adlı Constructor
statik bir yöntem olarak temsil edilir. Şimdi tamsayı kabul eden bir oluşturucuya sahip bir sınıf MyClass
düşünelim:
public class MyClass {
public MyClass(int value) {
this.Value = value;
}
...
}
Oluşturucu için bir dolgu türü, oluşturucuya geçirilen değerden bağımsız olarak, Değer alıcısı çağrıldığında gelecekteki her örnek -5 döndürecek şekilde ayarlanabilir:
// unit test code
ShimMyClass.ConstructorInt32 = (@this, value) => {
var shim = new ShimMyClass(@this) {
ValueGet = () => -5
};
};
Her dolgu türü iki tür oluşturucuyu kullanıma sunar. Yeni bir örnek gerektiğinde varsayılan oluşturucu kullanılmalıdır, ancak bağımsız değişken olarak bir dolgu örneği alan oluşturucu yalnızca oluşturucu dolgularında kullanılmalıdır:
// unit test code
public ShimMyClass() { }
public ShimMyClass(MyClass instance) : base(instance) { }
için ShimMyClass
oluşturulan türün yapısı aşağıdaki gibi gösterilebilir:
// Fakes generated code
public class ShimMyClass : ShimBase<MyClass>
{
public static Action<MyClass, int> ConstructorInt32 {
set {
...
}
}
public ShimMyClass() { }
public ShimMyClass(MyClass instance) : base(instance) { }
...
}
Temel üyelere erişme
Temel üyelerin dolgu özelliklerine, temel tür için dolgu oluşturularak ve alt örnek temel dolgu sınıfının oluşturucusuna girilerek ulaşılabilir.
Örneğin, örnek yöntemi MyMethod
ve alt türü MyChild
olan bir sınıf MyBase
düşünün:
public abstract class MyBase {
public int MyMethod() {
...
}
}
public class MyChild : MyBase {
}
Dolgusu MyBase
, yeni ShimMyBase
bir dolgu başlatılarak ayarlanabilir:
// unit test code
var child = new ShimMyChild();
new ShimMyBase(child) { MyMethod = () => 5 };
Temel dolgu oluşturucusunun parametresi olarak geçirildiğinde alt dolgu türünün örtük olarak alt örneğe dönüştürüldüğünü unutmayın.
ve için ShimMyChild
ShimMyBase
oluşturulan türün yapısı aşağıdaki koda benzetilebilir:
// Fakes generated code
public class ShimMyChild : ShimBase<MyChild> {
public ShimMyChild() { }
public ShimMyChild(Child child)
: base(child) { }
}
public class ShimMyBase : ShimBase<MyBase> {
public ShimMyBase(Base target) { }
public Func<int> MyMethod
{ set { ... } }
}
Statik oluşturucular
Dolgu türleri, bir türün statik oluşturucusunun dolgusu için statik bir yöntem StaticConstructor
sunar. Statik oluşturucular yalnızca bir kez yürütüleceğinden, türün herhangi bir üyesine erişilmeden önce dolgunun yapılandırıldığından emin olmanız gerekir.
Sonlandırıcılar
Sonlandırıcılar Fakes'ta desteklenmez.
Özel yöntemler
Fakes kod oluşturucusu, imzada yalnızca görünür türler olan özel yöntemler için dolgu özellikleri oluşturur, yani parametre türleri ve dönüş türü görünür.
Bağlama arabirimleri
Dolgulu bir tür bir arabirim uyguladığında, kod oluşturucu bu arabirimdeki tüm üyeleri aynı anda bağlamasına olanak tanıyan bir yöntem yayar.
Örneğin, uygulayan IEnumerable<int>
bir sınıf MyClass
verilir:
public class MyClass : IEnumerable<int> {
public IEnumerator<int> GetEnumerator() {
...
}
...
}
Bağlama yöntemini çağırarak MyClass içindeki uygulamalarını IEnumerable<int>
dolgulayabilirsiniz:
// unit test code
var shimMyClass = new ShimMyClass();
shimMyClass.Bind(new List<int> { 1, 2, 3 });
oluşturulan tür yapısı ShimMyClass
aşağıdaki koda benzer:
// Fakes generated code
public class ShimMyClass : ShimBase<MyClass> {
public ShimMyClass Bind(IEnumerable<int> target) {
...
}
}
Varsayılan Davranışı Değiştir
Oluşturulan her dolgu türü, özelliği aracılığıyla erişilebilen arabirimin bir örneğini IShimBehavior
ShimBase<T>.InstanceBehavior
içerir. Bu davranış, bir istemci açıkça dolgulanmamış bir örnek üyesini çağırdığı her durumda çağrılır.
Varsayılan olarak, belirli bir davranış ayarlanmamışsa statik ShimBehaviors.Current
özellik tarafından döndürülen örneği kullanır ve bu genellikle bir NotImplementedException
özel durum oluşturur.
Herhangi bir dolgu örneğinin özelliğini ayarlayarak InstanceBehavior
bu davranışı istediğiniz zaman değiştirebilirsiniz. Örneğin, aşağıdaki kod parçacığı hiçbir şey yapmak veya dönüş türünün varsayılan değerini döndürmek için davranışı değiştirir; örneğin: default(T)
// unit test code
var shim = new ShimMyClass();
//return default(T) or do nothing
shim.InstanceBehavior = ShimBehaviors.DefaultValue;
Statik özelliği ayarlayarakShimBehaviors.Current
, tüm titrek örneklerin InstanceBehavior
davranışını (özelliğin açıkça tanımlanmadığı durumlarda) genel olarak da değiştirebilirsiniz:
// unit test code
// change default shim for all shim instances where the behavior has not been set
ShimBehaviors.Current = ShimBehaviors.DefaultValue;
Dış Bağımlılıklarla Etkileşimleri Tanımlama
Kodunuzun dış sistemlerle veya bağımlılıklarla ne zaman etkileşimde olduğunu belirlemeye yardımcı olmak için (olarak environment
adlandırılır), dolguları kullanarak bir türün tüm üyelerine belirli bir davranış atayabilirsiniz. Bu statik yöntemleri içerir. Dolgu türünün statik Behavior
özelliğinde davranışı ayarlayarakShimBehaviors.NotImplemented
, bu türün açıkça dolgulanmamış bir üyesine yapılan tüm erişimler bir NotImplementedException
oluşturur. Bu, test sırasında kodunuzun dış sisteme veya bağımlılıklara erişmeye çalıştığına işaret eden yararlı bir sinyal görevi görebilir.
Birim test kodunuzda bunun nasıl ayarlanacağına yönelik bir örnek aşağıda verilmişti:
// unit test code
// Assign the NotImplementedException behavior to ShimMyClass
ShimMyClass.Behavior = ShimBehaviors.NotImplemented;
Kolaylık sağlamak için, aynı etkiyi elde etmek için bir kısaltma yöntemi de sağlanır:
// Shorthand to assign the NotImplementedException behavior to ShimMyClass
ShimMyClass.BehaveAsNotImplemented();
Dolgu Metotları İçinden Özgün Yöntemleri Çağırma
Dolgu yönteminin yürütülmesi sırasında özgün yöntemi yürütmeniz gerekebilecek senaryolar olabilir. Örneğin, yönteme geçirilen dosya adını doğruladıktan sonra dosya sistemine metin yazmak isteyebilirsiniz.
Bu durumu işlemeye yönelik yaklaşımlardan biri, aşağıdaki kodda gösterildiği gibi bir temsilci ve ShimsContext.ExecuteWithoutShims()
kullanarak özgün yönteme yapılan çağrıyı kapsüllemektir:
// unit test code
ShimFile.WriteAllTextStringString = (fileName, content) => {
ShimsContext.ExecuteWithoutShims(() => {
Console.WriteLine("enter");
File.WriteAllText(fileName, content);
Console.WriteLine("leave");
});
};
Alternatif olarak dolguyu null yapabilir, özgün yöntemi çağırabilir ve dolguyu geri yükleyebilirsiniz.
// unit test code
ShimsDelegates.Action<string, string> shim = null;
shim = (fileName, content) => {
try {
Console.WriteLine("enter");
// remove shim in order to call original method
ShimFile.WriteAllTextStringString = null;
File.WriteAllText(fileName, content);
}
finally
{
// restore shim
ShimFile.WriteAllTextStringString = shim;
Console.WriteLine("leave");
}
};
// initialize the shim
ShimFile.WriteAllTextStringString = shim;
Dolgu Türleriyle Eşzamanlılığı İşleme
Dolgu türleri AppDomain içindeki tüm iş parçacıklarında çalışır ve iş parçacığı benzitesine sahip değildir. Eşzamanlılığı destekleyen bir test çalıştırıcısı kullanmayı planlıyorsanız, bu özelliği aklınızda bulundurmanız çok önemlidir. Dolgu türlerini içeren testlerin eşzamanlı olarak çalıştırılamayacağına dikkat edin, ancak bu kısıtlama Fakes çalışma zamanı tarafından uygulanmaz.
Shimming System.Environment
Sınıfını dolgulu System.Environment hale getirmek istiyorsanız dosyada bazı değişiklikler mscorlib.fakes
yapmanız gerekir. Assembly öğesinin ardından aşağıdaki içeriği ekleyin:
<ShimGeneration>
<Add FullName="System.Environment"/>
</ShimGeneration>
Bu değişiklikleri yaptıktan ve çözümü yeniden derledikten sonra, sınıftaki System.Environment
yöntemler ve özellikler artık kullanılabilir. Aşağıda yöntemine nasıl davranış atayabileceğinize ilişkin bir örnek verilmişti GetCommandLineArgsGet
:
System.Fakes.ShimEnvironment.GetCommandLineArgsGet = ...
Bu değişiklikleri yaparak kodunuzun kapsamlı birim testi için temel bir araç olan sistem ortamı değişkenleriyle nasıl etkileşim kuracaklarını denetleme ve test etme olanağını ortaya çıkarmış olursunuz.