Pengubah akses "internal" C # saat melakukan pengujian unit


469

Saya baru dalam pengujian unit dan saya mencoba mencari tahu apakah saya harus mulai menggunakan lebih banyak pengubah akses 'internal'. Saya tahu bahwa jika kita menggunakan 'internal' dan mengatur variabel perakitan 'InternalsVisibleTo', kita dapat menguji fungsi yang tidak ingin kita umumkan dari proyek pengujian. Ini membuat saya berpikir bahwa saya harus selalu menggunakan 'internal' karena setidaknya setiap proyek (harus?) Memiliki proyek pengujian sendiri. Bisakah kalian memberi tahu saya alasan mengapa saya tidak melakukan ini? Kapan saya harus menggunakan 'pribadi'?


1
Layak disebutkan - Anda sering dapat menghindari kebutuhan unit menguji metode internal Anda dengan menggunakan System.Diagnostics.Debug.Assert()dalam metode itu sendiri.
Mike Marynowski

Jawaban:


1195

Kelas internal perlu diuji dan ada atribut assemby:

using System.Runtime.CompilerServices;

[assembly:InternalsVisibleTo("MyTests")]

Tambahkan ini ke file info proyek, mis Properties\AssemblyInfo.cs.


70
Tambahkan ke proyek yang sedang diuji (misalnya dalam Properties \ AssemblyInfo.cs). "MyTests" akan menjadi majelis uji.
EricSchaefer

86
Ini benar-benar jawaban yang diterima. Saya tidak tahu tentang kalian, tetapi ketika tes "terlalu jauh" dari kode yang mereka uji saya cenderung gugup. Saya semua untuk menghindari untuk menguji sesuatu yang ditandai private, tetapi terlalu banyak privatehal mungkin menunjuk ke internalkelas yang berjuang untuk diekstraksi. TDD atau tanpa TDD, saya lebih suka memiliki lebih banyak tes yang menguji banyak kode, daripada memiliki beberapa tes yang menggunakan jumlah kode yang sama. Dan menghindari menguji internalbarang-barang tidak benar-benar membantu untuk mencapai rasio yang baik.
sm

7
Ada diskusi besar yang terjadi antara @DerickBailey dan Dan Tao mengenai perbedaan semantik antara internal dan pribadi dan kebutuhan untuk menguji komponen internal . Layak dibaca.
Kris McGinnes

31
Dengan membungkus dan #if DEBUG, #endifblok akan mengaktifkan opsi ini hanya di build debug.
The Real Edward Cullen

17
Ini jawaban yang benar. Setiap jawaban yang mengatakan bahwa hanya metode publik yang harus diuji unit tidak ada gunanya tes unit dan membuat alasan. Pengujian fungsional berorientasi pada kotak hitam. Tes unit berorientasi kotak putih. Mereka harus menguji "unit" fungsionalitas, bukan hanya API publik.
Gunnar

127

Jika Anda ingin menguji metode pribadi, lihat PrivateObjectdan PrivateTypedi Microsoft.VisualStudio.TestTools.UnitTestingnamespace. Mereka menawarkan pembungkus yang mudah digunakan di sekitar kode refleksi yang diperlukan.

Documents: PrivateType , PrivateObject

Untuk VS2017 & 2019, Anda dapat menemukannya dengan mengunduh nuget MSTest.TestFramework


15
Saat memilih, silakan tinggalkan komentar. Terima kasih.
Brian Rasmussen

35
Sangat bodoh memilih jawaban ini. Ini menunjuk ke solusi baru dan benar-benar bagus yang tidak disebutkan sebelumnya.
Ignacio Soler Garcia

Rupanya, ada beberapa masalah dengan menggunakan TestFramework untuk penargetan aplikasi .net2.0 atau yang lebih baru: github.com/Microsoft/testfx/issues/366
Johnny Wu

41

Menambahkan ke jawaban Eric, Anda juga dapat mengkonfigurasi ini di csprojfile:

<ItemGroup>
    <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
      <_Parameter1>MyTests</_Parameter1>
    </AssemblyAttribute>
</ItemGroup>

Atau jika Anda memiliki satu proyek uji per proyek yang akan diuji, Anda dapat melakukan sesuatu seperti ini di Directory.Build.propsfile Anda :

<ItemGroup>
    <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
      <_Parameter1>$(MSBuildProjectName).Test</_Parameter1>
    </AssemblyAttribute>
</ItemGroup>

Lihat: https://stackoverflow.com/a/49978185/1678053
Contoh: https://github.com/gldraphael/evlog/blob/master/Directory.Build.props#L5-L12


2
Ini harus menjadi jawaban atas imo. Semua jawaban lain sangat ketinggalan jaman karena .net menjauh dari info assembly dan memindahkan fungsionalitas ke definisi csproj.
mBrice1024

12

Tetap menggunakan pribadi secara default. Jika seorang anggota tidak boleh diekspos di luar tipe itu, ia tidak boleh diekspos di luar tipe itu, bahkan di dalam proyek yang sama. Ini menjaga hal-hal lebih aman dan lebih rapi - ketika Anda menggunakan objek, itu lebih jelas metode mana yang Anda maksudkan untuk dapat digunakan.

Karena itu, saya pikir itu masuk akal untuk membuat metode pribadi-swasta internal untuk tujuan pengujian kadang-kadang. Saya lebih suka menggunakan refleksi, yang refactoring-tidak ramah.

Satu hal yang perlu dipertimbangkan mungkin sufiks "ForTest":

internal void DoThisForTest(string name)
{
    DoThis(name);
}

private void DoThis(string name)
{
    // Real implementation
}

Kemudian ketika Anda menggunakan kelas dalam proyek yang sama, sudah jelas (sekarang dan di masa depan) bahwa Anda seharusnya tidak benar-benar menggunakan metode ini - itu hanya ada untuk tujuan pengujian. Ini agak berantakan, dan bukan sesuatu yang saya lakukan sendiri, tapi setidaknya patut dipertimbangkan.


2
Jika metode ini internal, apakah ini tidak menghalangi penggunaannya dari unit pengujian?
Ralph Shillington

7
Saya kadang-kadang menggunakan ForTestpendekatan tetapi saya selalu menemukan itu mati jelek (menambahkan kode yang tidak memberikan nilai aktual dalam hal logika bisnis produksi). Biasanya saya menemukan saya harus menggunakan pendekatan karena desainnya agak disayangkan (yaitu harus mengatur ulang contoh tunggal di antara tes)
ChrisWue

1
Tergoda untuk melakukan downvote ini - apa perbedaan antara peretasan ini dan hanya membuat kelas internal alih-alih pribadi? Yah, setidaknya dengan persyaratan kompilasi. Maka itu menjadi sangat berantakan.
CAD berbicara

6
@CADbloke: Maksud Anda membuat metode internal daripada pribadi? Perbedaannya adalah jelas bahwa Anda benar - benar ingin menjadi pribadi. Setiap kode dalam basis kode produksi Anda yang memanggil metode dengan ForTestyang jelas salah, sedangkan jika Anda hanya membuat metode internal sepertinya baik-baik saja itu untuk digunakan.
Jon Skeet

2
@CADbloke: Anda bisa mengecualikan metode individual dalam rilis rilis semudah dalam file yang sama dengan menggunakan kelas parsial, IMO. Dan jika Anda melakukan itu, itu menunjukkan bahwa Anda tidak menjalankan tes Anda terhadap rilis rilis Anda, yang terdengar seperti ide yang buruk bagi saya.
Jon Skeet

11

Anda dapat menggunakan pribadi juga dan Anda dapat memanggil metode pribadi dengan refleksi. Jika Anda menggunakan Visual Studio Team Suite, ia memiliki beberapa fungsi bagus yang akan menghasilkan proxy untuk memanggil metode pribadi Anda untuk Anda. Berikut ini adalah artikel proyek kode yang menunjukkan bagaimana Anda dapat melakukan pekerjaan sendiri untuk menguji unit metode pribadi dan yang dilindungi:

http://www.codeproject.com/KB/cs/testnonpublicmembers.aspx

Dalam hal pengubah akses yang harus Anda gunakan, aturan umum saya adalah mulai dengan pribadi dan meningkat sesuai kebutuhan. Dengan cara itu Anda akan mengekspos detail internal kelas Anda seperti yang benar-benar dibutuhkan dan itu membantu menjaga detail implementasi tersembunyi, sebagaimana seharusnya.


3

Saya menggunakan Dotnet 3.1.101dan .csprojpenambahan yang berfungsi untuk saya adalah:

<PropertyGroup>
  <!-- Explicitly generate Assembly Info -->
  <GenerateAssemblyInfo>true</GenerateAssemblyInfo>
</PropertyGroup>

<ItemGroup>
  <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
  <_Parameter1>MyProject.Tests</_Parameter1>
  </AssemblyAttribute>
</ItemGroup>

Semoga ini bisa membantu seseorang di luar sana!


1
Tambahan yang secara eksplisit menghasilkan info perakitan adalah apa yang akhirnya membuatnya bekerja untuk saya juga. Terima kasih telah memposting ini!
thevioletsaber
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.