Saya baru-baru ini berdiskusi dengan beberapa teman yang mana dari 2 metode berikut ini yang terbaik untuk mematikan hasil atau panggilan ke metode di dalam kelas yang sama dari metode di dalam kelas yang sama.
Ini adalah contoh yang sangat sederhana. Pada kenyataannya fungsinya jauh lebih kompleks.
Contoh:
public class MyClass
{
public bool FunctionA()
{
return FunctionB() % 2 == 0;
}
protected int FunctionB()
{
return new Random().Next();
}
}
Jadi untuk menguji ini kita punya 2 metode.
Metode 1: Gunakan Fungsi dan Tindakan untuk mengganti fungsionalitas metode. Contoh:
public class MyClass
{
public Func<int> FunctionB { get; set; }
public MyClass()
{
FunctionB = FunctionBImpl;
}
public bool FunctionA()
{
return FunctionB() % 2 == 0;
}
protected int FunctionBImpl()
{
return new Random().Next();
}
}
[TestClass]
public class MyClassTests
{
private MyClass _subject;
[TestInitialize]
public void Initialize()
{
_subject = new MyClass();
}
[TestMethod]
public void FunctionA_WhenNumberIsOdd_ReturnsTrue()
{
_subject.FunctionB = () => 1;
var result = _subject.FunctionA();
Assert.IsFalse(result);
}
}
Metode 2: Buat anggota virtual, turunkan kelas dan di kelas turunan menggunakan Fungsi dan Tindakan untuk mengganti fungsionalitas Contoh:
public class MyClass
{
public bool FunctionA()
{
return FunctionB() % 2 == 0;
}
protected virtual int FunctionB()
{
return new Random().Next();
}
}
public class TestableMyClass
{
public Func<int> FunctionBFunc { get; set; }
public MyClass()
{
FunctionBFunc = base.FunctionB;
}
protected override int FunctionB()
{
return FunctionBFunc();
}
}
[TestClass]
public class MyClassTests
{
private TestableMyClass _subject;
[TestInitialize]
public void Initialize()
{
_subject = new TestableMyClass();
}
[TestMethod]
public void FunctionA_WhenNumberIsOdd_ReturnsTrue()
{
_subject.FunctionBFunc = () => 1;
var result = _subject.FunctionA();
Assert.IsFalse(result);
}
}
Saya ingin tahu yang lebih baik dan MENGAPA?
Pembaruan: CATATAN: FunctionB juga bisa bersifat publik
c#
design
unit-testing
tranceru1
sumber
sumber
FunctionA
mengembalikan bool tetapi hanya menetapkan variabel lokalx
dan tidak mengembalikan apa pun.public static
tetapi di kelas yang berbeda.FunctionB
rusak oleh desain.new Random().Next()
hampir selalu salah. Anda harus menyuntikkan instance dariRandom
. (Random
juga kelas yang dirancang dengan buruk, yang dapat menyebabkan beberapa masalah tambahan)Jawaban:
Diedit mengikuti pembaruan poster asli.
Penafian: bukan programmer C # (kebanyakan Java atau Ruby). Jawaban saya adalah: Saya tidak akan mengujinya sama sekali, dan saya pikir Anda tidak harus mengujinya.
Versi yang lebih panjang adalah: metode pribadi / yang dilindungi bukan bagian dari API, mereka pada dasarnya adalah pilihan implementasi, bahwa Anda dapat memutuskan untuk meninjau, memperbarui atau membuang sepenuhnya tanpa dampak apa pun di luar.
Saya kira Anda memiliki tes pada FunctionA (), yang merupakan bagian dari kelas yang terlihat dari dunia eksternal. Ini harus menjadi satu-satunya yang memiliki kontrak untuk diterapkan (dan yang dapat diuji). Metode pribadi Anda / dilindungi tidak memiliki kontrak untuk memenuhi dan / atau menguji.
Lihat diskusi terkait di sana: https://stackoverflow.com/questions/105007/should-i-test-private-methods-or-only-public-ones
Mengikuti komentar , jika FunctionB bersifat publik, saya hanya akan menguji keduanya menggunakan unit test. Anda mungkin berpikir bahwa tes FunctionA tidak sepenuhnya "unit" (seperti yang disebut FunctionB), tapi saya tidak akan terlalu khawatir dengan itu: jika tes FunctionB bekerja tetapi tidak tes FunctionA, itu berarti dengan jelas bahwa masalahnya bukan pada subdomain FunctionB, yang cukup baik bagi saya sebagai pembeda.
Jika Anda benar-benar ingin dapat benar-benar memisahkan dua tes, saya akan menggunakan semacam teknik mengejek untuk mengejek FunctionB saat menguji FunctionA (biasanya, mengembalikan nilai yang benar dikenal tetap). Saya tidak memiliki pengetahuan ekosistem C # untuk memberi saran pada perpustakaan mengejek yang spesifik, tetapi Anda dapat melihat pertanyaan ini .
sumber
MyClass
dan menimpa metode dengan fungsi yang ingin Anda rintisan. Mungkin juga merupakan ide yang baik untuk memperbarui pertanyaan Anda agarFunctionB
bisa dimasukkan ke publik.protected
metode adalah bagian dari permukaan publik suatu kelas, kecuali jika Anda memastikan bahwa tidak ada implementasi kelas Anda di majelis yang berbeda.Saya berlangganan teori bahwa jika suatu fungsi penting untuk diuji, atau penting untuk diganti, cukup penting untuk tidak menjadi detail implementasi pribadi dari kelas yang diuji, tetapi menjadi detail implementasi publik dari suatu kelas yang berbeda .
Jadi jika saya dalam skenario di mana saya miliki
Lalu saya akan refactor.
Sekarang saya memiliki skenario di mana D () dapat diuji secara independen, dan sepenuhnya dapat diganti.
Sebagai sarana organisasi, kolaborator saya mungkin tidak hidup di tingkat namespace yang sama. Misalnya, jika
A
ada di FooCorp.BLL, maka kolaborator saya mungkin memiliki lapisan lain, seperti di FooCorp.BLL.Collaborators (atau nama apa pun yang sesuai). Kolaborator saya mungkin hanya akan terlihat di dalam perakitan melaluiinternal
pengubah akses, yang kemudian akan saya paparkan ke proyek pengujian unit saya melaluiInternalsVisibleTo
atribut perakitan. Yang perlu diperhatikan adalah Anda masih bisa menjaga kebersihan API, sejauh yang dipikirkan penelepon, sambil menghasilkan kode yang dapat diverifikasi.sumber
Menambahkan pada apa yang Martin tunjukkan,
Jika metode Anda pribadi / dilindungi - jangan mengujinya. Ini internal ke kelas dan tidak boleh diakses di luar kelas.
Dalam kedua pendekatan yang Anda sebutkan, saya memiliki masalah ini -
Metode 1 - Ini benar-benar mengubah kelas yang diuji perilaku dalam tes.
Metode 2 - Ini sebenarnya tidak menguji kode produksi, melainkan menguji implementasi lain.
Dalam masalah yang dinyatakan, saya melihat bahwa satu-satunya logika A adalah untuk melihat apakah output FunctionB adalah genap. Meskipun ilustratif, FunctionB memberikan nilai acak, yang sulit untuk diuji.
Saya mengharapkan skenario realistis di mana kita dapat mengatur MyClass sehingga kita tahu apa yang akan dikembalikan FunctionB. Kemudian hasil yang diharapkan diketahui, kita dapat memanggil FunctionA dan menegaskan hasil yang sebenarnya.
sumber
protected
hampir sama denganpublic
. Hanyaprivate
daninternal
merupakan detail implementasi.internal protected
, menggunakan pembantu refleksi pribadi, atau membuat kelas turunan dalam proyek pengujian Anda.Saya pribadi menggunakan Method1 yaitu membuat semua metode menjadi Actions atau Funcs karena ini telah sangat meningkatkan testability kode untuk saya. Seperti halnya solusi apa pun ada pro dan kontra dengan pendekatan ini:
Pro
Cons
Jadi singkatnya, menggunakan Fungsi dan Aksi untuk tes Unit bagus jika Anda tahu bahwa kelas Anda tidak akan pernah ditimpa.
Juga, saya biasanya tidak membuat properti untuk Funcs tetapi sebaris langsung dengannya
Semoga ini membantu!
sumber
Menggunakan Mock itu mungkin. Nuget: https://www.nuget.org/packages/moq/
Dan percayalah, ini cukup sederhana dan masuk akal.
Mock membutuhkan metode virtual untuk menggantikannya.
sumber