Gunakan stub untuk mengisolasi bagian aplikasi Anda satu sama lain untuk pengujian unit
Jenis stub adalah teknologi penting yang disediakan oleh kerangka kerja Microsoft Fakes, memungkinkan isolasi komponen yang mudah Anda uji dari komponen lain yang diandalkannya. Stub bertindak sebagai bagian kecil kode yang menggantikan komponen lain selama pengujian. Manfaat utama menggunakan stub adalah kemampuan untuk mendapatkan hasil yang konsisten untuk mempermudah penulisan pengujian. Bahkan jika komponen lain belum berfungsi penuh, Anda masih dapat menjalankan pengujian dengan menggunakan stub.
Untuk menerapkan stub secara efektif, disarankan untuk merancang komponen Anda dengan cara yang terutama bergantung pada antarmuka daripada kelas konkret dari bagian lain aplikasi. Pendekatan desain ini mempromosikan pemisahan dan mengurangi kemungkinan perubahan dalam satu bagian yang membutuhkan modifikasi di bagian lain. Dalam hal pengujian, pola desain ini memungkinkan penggantian implementasi stub untuk komponen nyata, memfasilitasi isolasi yang efektif dan pengujian komponen target yang akurat.
Misalnya, mari kita pertimbangkan diagram yang mengilustrasikan komponen yang terlibat:
Dalam diagram ini, komponen yang sedang diuji adalah StockAnalyzer
, yang biasanya bergantung pada komponen lain yang disebut RealStockFeed
. Namun, RealStockFeed
menimbulkan tantangan untuk pengujian karena mengembalikan hasil yang berbeda setiap kali metodenya dipanggil. Varianbilitas ini menyulitkan untuk memastikan pengujian yang konsisten dan andal dari StockAnalyzer
.
Untuk mengatasi hambatan ini selama pengujian, kita dapat mengadopsi praktik injeksi dependensi. Pendekatan ini melibatkan penulisan kode Anda sedaya sehingga tidak secara eksplisit menyebutkan kelas di komponen lain dari aplikasi Anda. Sebagai gantinya, Anda menentukan antarmuka yang dapat diterapkan komponen lain dan stub untuk tujuan pengujian.
Berikut adalah contoh bagaimana Anda dapat menggunakan injeksi dependensi dalam kode Anda:
Batasan stub
Tinjau batasan berikut untuk stub.
Tanda tangan metode dengan pointer tidak didukung.
Kelas yang disegel atau metode statis tidak dapat di-stubbing menggunakan jenis stub karena jenis stub mengandalkan pengiriman metode virtual. Untuk kasus seperti itu, gunakan jenis shim seperti yang dijelaskan dalam Menggunakan shim untuk mengisolasi aplikasi Anda dari rakitan lain untuk pengujian unit
Membuat Stub: Panduan Langkah demi Langkah
Mari kita mulai latihan ini dengan contoh yang memotivasi: yang ditunjukkan dalam diagram sebelumnya.
Membuat Pustaka Kelas
Ikuti langkah-langkah ini untuk membuat pustaka kelas.
Buka Visual Studio dan buat proyek Pustaka Kelas.
Konfigurasikan atribut proyek:
- Atur Nama proyek ke StockAnalysis.
- Atur Nama solusi ke StubsTutorial.
- Atur kerangka kerja Target proyek ke .NET 8.0.
Hapus file default Class1.cs.
Tambahkan file baru bernama IStockFeed.cs dan salin dalam definisi antarmuka berikut:
Tambahkan file baru lain bernama StockAnalyzer.cs dan salin dalam definisi kelas berikut:
Membuat Proyek Pengujian
Buat proyek pengujian untuk latihan.
Klik kanan pada solusi dan tambahkan proyek baru bernama MSTest Test Project.
Atur nama proyek ke TestProject.
Atur kerangka kerja target proyek ke .NET 8.0.
Menambahkan rakitan Palsu
Tambahkan rakitan Palsu untuk proyek.
Tambahkan referensi proyek ke
StockAnalyzer
.Tambahkan Rakitan Palsu.
Di Penjelajah Solusi, temukan referensi perakitan:
Untuk Proyek .NET Framework yang lebih lama (gaya non-SDK), perluas node Referensi proyek pengujian unit Anda.
Untuk proyek gaya SDK yang menargetkan .NET Framework, .NET Core, atau .NET 5.0 atau yang lebih baru, perluas node Dependensi untuk menemukan rakitan yang ingin Anda palsukan di bawah Rakitan, Proyek, atau Paket.
Jika Anda bekerja di Visual Basic, pilih Perlihatkan Semua File di toolbar Penjelajah Solusi untuk melihat node Referensi.
Pilih rakitan yang berisi definisi kelas yang ingin Anda buat bongkahannya.
Pada menu pintasan, pilih Tambahkan Rakitan Fake.
Membuat proyek pengujian unit
Sekarang buat pengujian unit.
Ubah file default UnitTest1.cs untuk menambahkan definisi berikut
Test Method
.[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); } }
Bagian khusus sihir di sini adalah
StubIStockFeed
kelas. Untuk setiap antarmuka dalam rakitan yang dirujuk, mekanisme Microsoft Fakes menghasilkan kelas stub. Nama kelas stub berasal dari nama antarmuka, dengan "Fakes.Stub
" sebagai prefiks, dan nama jenis parameter ditambahkan.Stub juga dihasilkan untuk getter dan setter properti, untuk peristiwa, dan untuk metode generik. Untuk informasi selengkapnya, lihat Menggunakan stub untuk mengisolasi bagian aplikasi Anda satu sama lain untuk pengujian unit.
Buka Test Explorer dan jalankan pengujian.
Stub untuk berbagai jenis anggota jenis
Ada stub untuk berbagai jenis anggota jenis.
Metode
Dalam contoh yang disediakan, metode dapat di-stubbing dengan melampirkan delegasi ke instans kelas stub. Nama jenis stub berasal dari nama metode dan parameter. Misalnya, pertimbangkan antarmuka berikut IStockFeed
dan metodenya GetSharePrice
:
// IStockFeed.cs
interface IStockFeed
{
int GetSharePrice(string company);
}
Kami melampirkan stub ke GetSharePrice
dengan menggunakan GetSharePriceString
:
// 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;
}
});
Jika Anda tidak menyediakan stub untuk metode , Fakes menghasilkan fungsi yang mengembalikan default value
jenis pengembalian. Untuk angka, nilai defaultnya adalah 0. Untuk jenis kelas, defaultnya ada null
di C# atau Nothing
di Visual Basic.
Properti
Pencari properti dan setter diekspos sebagai delegasi terpisah dan dapat disandingkan satu per satu. Misalnya, pertimbangkan Value
properti dari IStockFeedWithProperty
:
interface IStockFeedWithProperty
{
int Value { get; set; }
}
Untuk membuat stub getter dan setter dan Value
mensimulasikan properti otomatis, Anda dapat menggunakan kode berikut:
// unit test code
int i = 5;
var stub = new StubIStockFeedWithProperty();
stub.ValueGet = () => i;
stub.ValueSet = (value) => i = value;
Jika Anda tidak menyediakan metode stub untuk setter atau getter properti, Fakes menghasilkan stub yang menyimpan nilai, membuat properti stub berperilaku seperti variabel sederhana.
Aktivitas
Peristiwa diekspos sebagai bidang delegasi, memungkinkan peristiwa yang tersandung dinaikkan hanya dengan memanggil bidang dukungan peristiwa. Mari kita pertimbangkan antarmuka berikut untuk stub:
interface IStockFeedWithEvents
{
event EventHandler Changed;
}
Untuk menaikkan Changed
acara, Anda memanggil delegasi backing:
// unit test code
var withEvents = new StubIStockFeedWithEvents();
// raising Changed
withEvents.ChangedEvent(withEvents, EventArgs.Empty);
Metode generik
Anda dapat membagi metode generik dengan menyediakan delegasi untuk setiap instansiasi metode yang diinginkan. Misalnya, mengingat antarmuka berikut dengan metode generik:
interface IGenericMethod
{
T GetValue<T>();
}
Anda dapat membasahi GetValue<int>
instansiasi sebagai berikut:
[TestMethod]
public void TestGetValue()
{
var stub = new StubIGenericMethod();
stub.GetValueOf1<int>(() => 5);
IGenericMethod target = stub;
Assert.AreEqual(5, target.GetValue<int>());
}
Jika kode memanggil dengan instansiasi GetValue<T>
lain, stub menjalankan perilaku.
Stub kelas virtual
Dalam contoh sebelumnya, stub telah dihasilkan dari antarmuka. Namun, Anda juga dapat menghasilkan stub dari kelas yang memiliki anggota virtual atau abstrak. Misalnya:
// 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;
}
}
Dalam stub yang dihasilkan dari kelas ini, Anda dapat mengatur metode delegasi untuk DoAbstract()
dan DoVirtual()
, tetapi tidak DoConcrete()
.
// unit test
var stub = new Fakes.MyClass();
stub.DoAbstractString = (x) => { Assert.IsTrue(x>0); };
stub.DoVirtualInt32 = (n) => 10 ;
Jika Anda tidak memberikan delegasi untuk metode virtual, Fakes dapat memberikan perilaku default atau memanggil metode di kelas dasar. Agar metode dasar dipanggil, atur CallBase
properti :
// 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));
Mengubah perilaku default
Setiap jenis stub yang dihasilkan menyimpan instans IStubBehavior
antarmuka melalui IStub.InstanceBehavior
properti . Perilaku ini dipanggil setiap kali klien memanggil anggota tanpa delegasi kustom yang terlampir. Jika perilaku tidak diatur, ia menggunakan instans yang dikembalikan oleh StubsBehaviors.Current
properti . Secara default, properti ini mengembalikan perilaku yang melemparkan pengecualian NotImplementedException
.
Anda dapat mengubah perilaku kapan saja dengan mengatur InstanceBehavior
properti pada instans stub apa pun. Misalnya, cuplikan berikut mengubah perilaku sehingga stub tidak melakukan apa pun atau mengembalikan nilai default dari jenis default(T)
pengembalian :
// unit test code
var stub = new StockAnalysis.Fakes.StubIStockFeed();
// return default(T) or do nothing
stub.InstanceBehavior = StubsBehaviors.DefaultValue;
Perilaku juga dapat diubah secara global untuk semua objek stub di mana perilaku tidak diatur dengan StubsBehaviors.Current
properti :
// Change default behavior for all stub instances where the behavior has not been set.
StubBehaviors.Current = BehavedBehaviors.DefaultValue;