Bagaimana Anda menguji metode privat dengan NUnit?

104

Saya bertanya-tanya bagaimana cara menggunakan NUnit dengan benar. Pertama, saya membuat proyek uji terpisah yang menggunakan proyek utama saya sebagai referensi. Tetapi dalam kasus itu, saya tidak dapat menguji metode privat. Dugaan saya adalah bahwa saya perlu memasukkan kode tes saya ke dalam kode utama saya ?! - Sepertinya itu bukan cara yang benar untuk melakukannya. (Saya tidak menyukai gagasan kode pengiriman dengan tes di dalamnya.)

Bagaimana Anda menguji metode privat dengan NUnit?

MrFox
sumber

Jawaban:

74

Umumnya, pengujian unit membahas antarmuka publik kelas, dengan teori bahwa penerapannya tidak material, selama hasilnya benar dari sudut pandang klien.

Jadi, NUnit tidak menyediakan mekanisme apa pun untuk menguji anggota non-publik.

harpo
sumber
1
+1 Saya baru saja menemukan masalah ini dan dalam kasus saya ada algoritme "pemetaan" yang terjadi di antara privat dan publik yang berarti jika saya melakukan Uji Unit publik, itu sebenarnya akan menjadi tes integrasi. Dalam skenario ini saya pikir itu mencoba untuk menulis poin tes ke masalah desain dalam kode. dalam hal ini saya mungkin harus membuat kelas berbeda yang melakukan metode "pribadi" itu.
andy
140
Saya tidak sepenuhnya setuju dengan argumen ini. Pengujian unit bukan tentang kelas, ini tentang kode . Jika saya memiliki kelas dengan satu metode publik, dan sepuluh metode privat digunakan untuk membuat hasil dari metode publik, saya tidak memiliki cara untuk memastikan setiap metode privat bekerja dengan cara yang dimaksudkan. Saya dapat mencoba membuat kasus uji pada metode publik yang mengenai metode privat yang tepat dengan cara yang benar. Tapi saya kemudian tidak tahu apakah metode privat benar-benar dipanggil, kecuali saya percaya metode publik tidak pernah berubah. Metode pribadi dimaksudkan untuk menjadi pribadi bagi pengguna kode produksi, bukan penulisnya.
Quango
3
@Quango, dalam pengaturan pengujian standar, "penulis" adalah pengguna produksi kode. Namun demikian, jika Anda tidak mencium masalah desain, Anda memiliki opsi untuk menguji metode non-publik tanpa mengubah kelas. System.Reflectionmemungkinkan Anda untuk mengakses dan memanggil metode non-publik menggunakan bendera yang mengikat, sehingga Anda dapat meretas NUnit atau menyiapkan kerangka kerja Anda sendiri. Atau (lebih mudah, menurut saya), Anda dapat menyiapkan tanda waktu kompilasi (#Jika PENGUJIAN) untuk mengubah pengubah akses, memungkinkan Anda menggunakan kerangka kerja yang ada.
harpo
23
Metode pribadi adalah detail implementasi. Banyak manfaat dari pengujian unit adalah untuk memungkinkan pemfaktoran ulang implementasi tanpa mengubah perilakunya . Jika metode privat menjadi begitu rumit sehingga seseorang ingin menutupinya dengan pengujian secara individual, maka lemma ini dapat digunakan: "Metode privat selalu dapat menjadi metode publik (atau internal) dari kelas terpisah". Artinya, jika sepotong logika cukup maju untuk diuji, kemungkinan itu adalah kandidat untuk menjadi kelasnya sendiri, dengan pengujiannya sendiri.
Anders Forsgren
6
@AndersForsgren Tapi titik pengujian unit , yang bertentangan dengan pengujian integrasi atau fungsional , justru untuk menguji detail tersebut. Dan cakupan kode dianggap sebagai metrik pengujian unit yang penting, jadi dalam teori setiap bagian logika "cukup maju untuk dapat diuji".
Kyle Strand
57

Meskipun saya setuju bahwa fokus pengujian unit haruslah antarmuka publik, Anda mendapatkan kesan kode yang jauh lebih terperinci jika Anda juga menguji metode pribadi. Kerangka pengujian MS memungkinkan hal ini melalui penggunaan PrivateObject dan PrivateType, NUnit tidak. Yang saya lakukan adalah:

private MethodInfo GetMethod(string methodName)
{
    if (string.IsNullOrWhiteSpace(methodName))
        Assert.Fail("methodName cannot be null or whitespace");

    var method = this.objectUnderTest.GetType()
        .GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance);

    if (method == null)
        Assert.Fail(string.Format("{0} method not found", methodName));

    return method;
}

Dengan cara ini berarti Anda tidak perlu berkompromi dengan enkapsulasi demi kemudahan pengujian. Ingatlah bahwa Anda harus memodifikasi BindingFlags jika Anda ingin menguji metode statis privat. Contoh di atas hanyalah untuk metode contoh.

pengguna1039513
sumber
7
Menguji metode pembantu kecil tersebut menangkap bagian unit dalam pengujian unit. Tidak semua cocok untuk kelas utilitas juga.
Johan Larsson
12
Inilah yang saya butuhkan. Terima kasih! Yang saya butuhkan hanyalah memeriksa bahwa bidang pribadi disetel dengan benar, dan saya TIDAK perlu menghabiskan 3 jam untuk mencari tahu cara menentukannya melalui antarmuka publik. Ini adalah kode yang sudah ada yang tidak dapat difaktorkan ulang secara sembarangan. Sungguh lucu bagaimana pertanyaan sederhana dapat dijawab dengan dogma idealis ...
Droj
5
Ini harus menjadi jawaban yang dipilih, karena ini adalah satu-satunya jawaban yang benar-benar menjawab pertanyaan.
bavaza
6
Sepakat. Jawaban yang dipilih memiliki kelebihan, tetapi merupakan jawaban untuk "haruskah saya menguji metode pribadi", bukan pertanyaan OP.
chesterbr
2
berhasil. Terima kasih! Inilah yang dicari banyak orang. Ini harus menjadi jawaban yang dipilih,
Able Johnson
47

Pola umum untuk menulis tes unit adalah dengan hanya menguji metode publik.

Jika Anda menemukan bahwa Anda memiliki banyak metode privat yang ingin Anda uji, biasanya ini adalah tanda bahwa Anda harus mengubah kode Anda.

Salah jika membuat metode ini menjadi publik di kelas tempat mereka tinggal saat ini. Itu akan memutuskan kontrak yang Anda ingin kelas itu miliki.

Mungkin benar untuk memindahkan mereka ke kelas helper dan menjadikannya publik di sana. Kelas ini mungkin tidak diekspos oleh API Anda.

Dengan cara ini kode pengujian tidak pernah dicampur dengan kode publik Anda.

Masalah serupa sedang menguji kelas privat yaitu. kelas yang tidak Anda ekspor dari perakitan Anda. Dalam hal ini Anda dapat secara eksplisit menjadikan perakitan kode pengujian Anda sebagai teman dari perakitan kode produksi menggunakan atribut InternalsVisibleTo.

morechilli.dll
sumber
1
+ 1 ya! Saya baru saja sampai pada kesimpulan itu saat menulis tes saya, saran bagus!
andy
1
Memindahkan metode pribadi yang ingin Anda uji tetapi tidak mengekspos pengguna API ke kelas berbeda yang tidak terekspos dan menjadikannya publik, itulah yang saya cari.
Thomas N
terima kasih @morechilli. Belum pernah melihat InternalsVisibleTo sebelumnya. Membuat pengujian jadi lebih mudah.
Andrew MacNaughton
Hai. Baru-baru ini menerima beberapa suara negatif tanpa komentar. Berikan umpan balik untuk menjelaskan pemikiran atau kekhawatiran Anda.
morechi
6
Jadi jika Anda memiliki metode privat yang dipanggil di banyak tempat dalam sebuah kelas untuk melakukan sesuatu yang sangat spesifik yang hanya dibutuhkan oleh kelas itu dan itu tidak akan diekspos sebagai bagian dari API publik ... maksudnya taruh di helper kelas hanya untuk menguji? Sebaiknya Anda membuatnya menjadi publik untuk kelas.
Daniel Macias
21

Dimungkinkan untuk menguji metode privat dengan mendeklarasikan rakitan uji Anda sebagai rakitan teman dari rakitan target yang Anda uji. Lihat tautan di bawah untuk detailnya:

http://msdn.microsoft.com/en-us/library/0tke9fxk.aspx

Ini dapat berguna karena sebagian besar memisahkan kode pengujian dari kode produksi Anda. Saya sendiri tidak pernah menggunakan metode ini karena saya tidak pernah merasa membutuhkannya. Saya kira Anda dapat menggunakannya untuk mencoba dan menguji kasus uji ekstrim yang tidak dapat Anda tiru di lingkungan pengujian untuk melihat bagaimana kode Anda menanganinya.

Seperti yang telah dikatakan, Anda tidak perlu menguji metode privat. Anda lebih dari likley ingin mengubah kode Anda menjadi blok bangunan yang lebih kecil. Salah satu tip yang mungkin membantu Anda ketika Anda datang ke refactor adalah mencoba dan memikirkan tentang domain yang berhubungan dengan sistem Anda dan memikirkan tentang objek 'nyata' yang menghuni domain ini. Objek / kelas Anda di sistem Anda harus berhubungan langsung dengan objek nyata yang akan memungkinkan Anda untuk mengisolasi perilaku yang tepat yang harus dimiliki objek dan juga membatasi tanggung jawab objek. Ini berarti bahwa Anda melakukan refactoring secara logis daripada hanya untuk memungkinkan pengujian metode tertentu; Anda akan dapat menguji perilaku objek.

Jika Anda masih merasa perlu untuk menguji internal maka Anda mungkin juga ingin mempertimbangkan untuk mengejek dalam pengujian Anda karena Anda ingin fokus pada satu bagian kode. Mocking adalah tempat Anda memasukkan dependensi objek ke dalamnya tetapi objek yang dimasukkan bukanlah objek 'nyata' atau produksi. Mereka adalah objek dummy dengan perilaku hardcode untuk memudahkan mengisolasi kesalahan perilaku. Rhino.Mocks adalah kerangka kerja mengejek gratis populer yang pada dasarnya akan menulis objek untuk Anda. TypeMock.NET (produk komersial dengan edisi komunitas tersedia) adalah kerangka kerja yang lebih kuat yang dapat meniru objek CLR. Sangat berguna untuk mengejek kelas SqlConnection / SqlCommand dan Datatable misalnya saat menguji aplikasi database.

Semoga jawaban ini memberi Anda sedikit lebih banyak informasi untuk memberi tahu Anda tentang Pengujian Unit secara umum dan membantu Anda mendapatkan hasil yang lebih baik dari Pengujian Unit.

Dafydd Giddins
sumber
12
Dimungkinkan untuk menguji metode internal , bukan pribadi (dengan NUnit).
TrueWill
6

Saya lebih suka memiliki kemampuan untuk menguji metode privat. Ketika xUnit dimulai, itu dimaksudkan untuk menguji fungsionalitas setelah kode ditulis. Menguji antarmuka sudah cukup untuk tujuan ini.

Pengujian unit telah berkembang menjadi pengembangan yang digerakkan oleh pengujian. Memiliki kemampuan untuk menguji semua metode berguna untuk aplikasi itu.

Mark Glass
sumber
5

Pertanyaan ini ada di tahun-tahun yang sudah lanjut, tetapi saya pikir saya akan membagikan cara saya melakukan ini.

Pada dasarnya, saya memiliki semua kelas pengujian unit saya dalam rakitan yang mereka uji dalam ruang nama 'UnitTest' di bawah 'default' untuk rakitan itu - setiap file uji dibungkus dalam:

#if DEBUG

...test code...

#endif

memblokir, dan semua itu berarti bahwa a) itu tidak didistribusikan dalam rilis dan b) saya dapat menggunakan internal/ Friendlevel deklarasi tanpa lompatan lingkaran.

Hal lain yang ditawarkan ini, yang lebih berkaitan dengan pertanyaan ini, adalah penggunaan partialkelas, yang dapat digunakan untuk membuat proxy untuk menguji metode privat, jadi misalnya untuk menguji sesuatu seperti metode privat yang mengembalikan nilai integer:

public partial class TheClassBeingTested
{
    private int TheMethodToBeTested() { return -1; }
}

di kelas utama assembly, dan kelas pengujian:

#if DEBUG

using NUnit.Framework;

public partial class TheClassBeingTested
{
    internal int NUnit_TheMethodToBeTested()
    {
        return TheMethodToBeTested();
    }
}

[TestFixture]
public class ClassTests
{
    [Test]
    public void TestMethod()
    {
        var tc = new TheClassBeingTested();
        Assert.That(tc.NUnit_TheMethodToBeTested(), Is.EqualTo(-1));
    }
}

#endif

Jelas, Anda perlu memastikan bahwa Anda tidak menggunakan metode ini saat mengembangkan, meskipun rilis build akan segera menunjukkan panggilan yang tidak disengaja jika Anda melakukannya.

Stuart Wood
sumber
4

Tujuan utama pengujian unit adalah untuk menguji metode publik suatu kelas. Metode publik tersebut akan menggunakan metode privat tersebut. Pengujian unit akan menguji perilaku dari apa yang tersedia untuk umum.

Maxime Rouiller
sumber
3

Mohon maaf jika ini tidak menjawab pertanyaan tetapi solusi seperti menggunakan refleksi, pernyataan #if #endif atau membuat metode privat terlihat tidak menyelesaikan masalah. Ada beberapa alasan mengapa metode privat tidak terlihat ... bagaimana jika itu kode produksi dan tim secara retrospektif menulis pengujian unit misalnya.

Untuk proyek yang saya kerjakan hanya MSTest (sayangnya) tampaknya memiliki cara, menggunakan pengakses, untuk menguji unit metode pribadi.

Ritesh
sumber
2

Anda tidak menguji fungsi pribadi. Ada cara untuk menggunakan refleksi untuk masuk ke metode dan properti pribadi. Tetapi itu tidak terlalu mudah dan saya sangat melarang praktik ini.

Anda seharusnya tidak menguji apa pun yang tidak bersifat publik.

Jika Anda memiliki beberapa metode dan properti internal, Anda harus mempertimbangkan untuk mengubahnya menjadi publik, atau mengirimkan pengujian Anda dengan aplikasi (sesuatu yang saya tidak benar-benar melihat sebagai masalah).

Jika pelanggan Anda dapat menjalankan Test-Suite dan melihat bahwa kode yang Anda kirimkan benar-benar "berfungsi", saya tidak melihat ini sebagai masalah (selama Anda tidak memberikan IP Anda melalui ini). Hal-hal yang saya sertakan dalam setiap rilis adalah laporan pengujian dan laporan cakupan kode.

Tigraine
sumber
Beberapa pelanggan mencoba menjaga anggaran tetap kecil dan memulai diskusi tentang cakupan pengujian. Sulit untuk menjelaskan kepada orang-orang ini bahwa tes menulis bukan karena Anda seorang pembuat kode yang buruk dan tidak mempercayai keahlian Anda sendiri.
MrFox
1

Dalam teori Unit Testing, hanya kontrak yang harus diuji. yaitu hanya anggota umum kelas. Namun dalam praktiknya developer biasanya ingin menguji anggota internal untuk. - dan itu tidak buruk. Ya, ini bertentangan dengan teori, tetapi dalam praktiknya kadang-kadang bisa berguna.

Jadi, jika Anda benar-benar ingin menguji anggota internal, Anda dapat menggunakan salah satu pendekatan ini:

  1. Jadikan anggota Anda publik. Dalam banyak buku, penulis menyarankan pendekatan ini sesederhana mungkin
  2. Anda dapat menjadikan Anda anggota internal dan menambahkan InternalVisibleTo ke assebly
  3. Anda dapat membuat anggota kelas terlindungi dan mewarisi kelas pengujian Anda dari kelas di bawah pengujian Anda.

Contoh kode (pseudo code):

public class SomeClass
{
    protected int SomeMethod() {}
}
[TestFixture]
public class TestClass : SomeClass{

    protected void SomeMethod2() {}
    [Test]
    public void SomeMethodTest() { SomeMethod2(); }
}
burzhuy
sumber
Tampaknya kecoa tersembunyi di dalam kode contoh Anda;) Metode di dalam perlengkapan NUnit harus publik, jika tidak, Anda akan mendapatkannya Message: Method is not public.
Gucu112
@ Gucu112 Terima kasih. Aku telah memperbaikinya. Secara umum tidak masalah karena tujuannya adalah untuk menunjukkan pendekatan dari desain pov.
burzhuy
1

Anda dapat membuat metode Anda dilindungi secara internal, kemudian menggunakan assembly: InternalsVisibleTo("NAMESPACE") namespace pengujian Anda.

Karenanya, TIDAK! Anda tidak dapat mengakses metode privat, tetapi ini adalah solusi.

Furgalicious
sumber
0

Saya akan membuat paket metode privat terlihat. Dengan cara itu Anda membuatnya cukup pribadi sambil tetap dapat menguji metode tersebut. Saya tidak setuju dengan orang-orang yang mengatakan bahwa antarmuka publik adalah satu-satunya yang harus diuji. Seringkali ada kode yang sangat penting dalam metode privat yang tidak dapat diuji dengan benar hanya melalui antarmuka eksternal.

Jadi intinya adalah jika Anda lebih peduli tentang kode yang benar atau menyembunyikan informasi. Saya akan mengatakan visibilitas paket adalah kompromi yang baik karena untuk mengakses metode tersebut seseorang harus menempatkan kelas mereka di paket Anda. Itu seharusnya membuat mereka berpikir dua kali tentang apakah itu hal yang benar-benar cerdas untuk dilakukan.

Saya seorang pria Java btw, jadi visiblilty paket mungkin disebut sesuatu yang sama sekali berbeda di C #. Cukuplah untuk mengatakan bahwa itu adalah saat dua kelas harus berada di namespace yang sama untuk mengakses metode tersebut.

Fylke
sumber