Bagaimana Anda mengejek sistem file dalam C # untuk pengujian unit?

149

Apakah ada perpustakaan atau metode untuk mengejek sistem file dalam C # untuk menulis tes unit? Dalam kasus saya saat ini, saya memiliki metode yang memeriksa apakah file tertentu ada dan membaca tanggal pembuatan. Saya mungkin membutuhkan lebih dari itu di masa depan.

pupeno
sumber
1
Ini terlihat seperti duplikat dari beberapa yang lain, termasuk: stackoverflow.com/questions/664277/… .
John Saunders
Mungkin mencoba melihat ke pex ( research.microsoft.com/en-us/projects/pex/filesystem.pdf )
Tinus
2
@Mitch: Sebagian besar waktu, cukup untuk menempatkan data dalam sistem file dan membiarkan unit test berjalan. Namun, saya telah menemukan metode yang menjalankan banyak operasi IO, dan pengaturan lingkungan pengujian untuk metode tersebut sangat disederhanakan dengan menggunakan sistem file tiruan.
Steve Guidi
Saya menulis github.com/guillaume86/VirtualPath untuk tujuan itu (dan banyak lagi), itu masih WIP dan API pasti akan berubah tetapi sudah berfungsi, dan beberapa tes disertakan.
Guillaume86

Jawaban:

154

Sunting: Instal paket NuGet System.IO.Abstractions.

Paket ini tidak ada ketika jawaban ini awalnya diterima. Jawaban asli diberikan untuk konteks historis di bawah ini:

Anda bisa melakukannya dengan membuat antarmuka:

interface IFileSystem {
    bool FileExists(string fileName);
    DateTime GetCreationDate(string fileName);
}

dan membuat implementasi 'nyata' yang menggunakan System.IO.File.Exists () dll. Anda kemudian dapat mengejek antarmuka ini menggunakan kerangka kerja mengejek; Saya merekomendasikan Moq .

Sunting: seseorang melakukan ini dan silakan mempostingnya secara online di sini .

Saya telah menggunakan pendekatan ini untuk mengejek DateTime.UtcNow di antarmuka IClock (benar-benar sangat berguna untuk pengujian kami untuk dapat mengontrol aliran waktu!), Dan lebih tradisional, antarmuka ISqlDataAccess.

Pendekatan lain mungkin menggunakan TypeMock , ini memungkinkan Anda untuk mencegat panggilan ke kelas dan mematikannya. Namun ini membutuhkan biaya, dan perlu diinstal pada PC seluruh tim Anda dan server build Anda untuk dapat berjalan, juga, tampaknya tidak akan bekerja untuk System.IO.File, karena tidak dapat mematikan mscorlib .

Anda juga bisa hanya menerima bahwa metode tertentu tidak dapat diuji unit dan mengujinya dalam suite tes integrasi / sistem yang berjalan lambat terpisah.

Matt Howells
sumber
1
Menurut pendapat saya, membuat antarmuka seperti yang dijelaskan Matt di sini adalah caranya. Saya bahkan telah menulis alat yang menghasilkan antarmuka seperti itu untuk Anda, yang berguna ketika mencoba untuk mengejek kelas statis dan / atau disegel, atau metode yang non-deterministik (yaitu jam dan generator nomor acak). Lihat jolt.codeplex.com untuk informasi lebih lanjut.
Steve Guidi
Sepertinya repo di artikel yang direferensikan telah dihapus / dipindahkan tanpa pemberitahuan. Namun, tampaknya ada paket nuget dari upayanya di sini: nuget.org/packages/mscorlib-mock
Mike-E
Typemock memang memiliki batasan pada jenis apa yang dapat dipalsukan tetapi (setidaknya dalam versi saat ini per Oktober 2017) Anda pasti dapat memalsukan kelas File statis. Saya baru saja memverifikasi ini sendiri.
Ryan Rodemoyer
Bisakah Anda meringkaskan beberapa suite tes integrasi?
Ozkan
83

Instal-Paket Sistem.IO.Abstraksi

Pustaka imajiner ini ada sekarang, ada paket NuGet untuk System.IO.Abstractions , yang memisahkan abstrak namespace System.IO.

Ada juga satu set alat bantu pengujian, System.IO.Abstractions.TestingHelpers yang - pada saat penulisan - hanya dilaksanakan sebagian, tetapi merupakan titik awal yang sangat baik.

Biner Terburuk
sumber
3
Saya pikir standar di sekitar abstraksi yang sudah dibangun ini adalah yang terbaik. Belum pernah mendengar tentang perpustakaan itu, jadi terima kasih banyak untuk informasi ini.
julealgon
PM adalah singkatan dari Package Manager .. untuk membuka ... Tools> NuGet Package Manager> Package Manager Console
thedanotto
11

Anda mungkin harus membuat kontrak untuk menentukan hal-hal apa yang Anda butuhkan dari sistem file dan kemudian menulis pembungkus di sekitar fungsi-fungsi itu. Pada titik itu Anda bisa mengejek atau mematikan implementasi.

Contoh:

interface IFileWrapper { bool Exists(String filePath); }

class FileWrapper: IFileWrapper
{
    bool Exists(String filePath) { return File.Exists(filePath); }        
}

class FileWrapperStub: IFileWrapper
{
    bool Exists(String filePath) 
    { return (filePath == @"C:\myfilerocks.txt"); }
}
Joseph
sumber
5

Rekomendasi saya adalah menggunakan http://systemwrapper.codeplex.com/ karena menyediakan pembungkus untuk jenis yang paling banyak digunakan di System namespace

adeel41
sumber
Saat ini saya menggunakan perpustakaan ini, dan sekarang saya telah menemukan bahwa abstraksi untuk hal-hal seperti FileStream tidak termasuk IDisposable, saya sedang mencari penggantinya. Jika perpustakaan tidak mengizinkan saya untuk membuang aliran dengan benar, maka saya tidak bisa merekomendasikan (atau menggunakannya) untuk menangani operasi semacam itu.
James Nail
1
IFileStreamWrap dari SystemWrapper mengimplementasikan IDisposable sekarang.
kisah
systemwrapper adalah .net framework saja, itu akan menyebabkan masalah aneh jika digunakan dengan .netcore
Adil H. Raza
3

Saya menemukan solusi berikut untuk ini:

  • Tulis tes Integrasi, bukan tes unit. Agar ini berfungsi, Anda memerlukan cara sederhana untuk membuat folder tempat Anda dapat membuang barang tanpa perlu khawatir akan mengganggu pengujian lainnya. Saya memiliki kelas TestFolder sederhana yang dapat membuat folder metode pengujian unik untuk digunakan.
  • Tulis System.IO.File yang dapat dipermainkan. Itu membuat IFile.cs . Saya menemukan menggunakan ini sering berakhir dengan tes yang hanya membuktikan Anda dapat menulis pernyataan mengejek, tetapi jangan menggunakannya ketika penggunaan IO kecil.
  • Periksa lapisan abstraksi Anda, dan ekstrak file IO dari kelas. Buat antarmuka untuk ini. Sisanya menggunakan tes integrasi (tetapi ini akan sangat kecil). Ini berbeda dari di atas dalam hal itu daripada melakukan file. Baca Anda menulis maksudnya, katakan ioThingie.loadSettings ()
  • System.IO.bstraksi . Saya belum pernah menggunakan ini, tapi ini yang paling saya sukai.

Saya akhirnya menggunakan semua metode di atas, tergantung pada apa yang saya tulis. Tetapi sebagian besar waktu saya akhirnya berpikir abstraksi salah ketika saya menulis unit test yang mengenai IO.

Michael Lloyd Lee mlk
sumber
4
Tautan ke IFile.cs rusak.
Mike-E
3

Dengan menggunakan System.IO.Abstractions dan System.IO.Abstractions.TestingHelpers seperti itu:

public class ManageFile {
   private readonly IFileSystem _fileSystem;
   public ManageFile(IFileSystem fileSystem){

      _fileSystem = fileSystem;
   }

   public bool FileExists(string filePath){}
       if(_fileSystem.File.Exists(filePath){
          return true;
       }
       return false;
   }
}

Di Kelas Tes Anda, Anda menggunakan MockFileSystem () untuk mengejek file dan Anda instanciate ManageFile seperti:

var mockFileSysteme = new MockFileSystem();
var mockFileData = new MockFileData("File content");
mockFileSysteme.AddFile(mockFilePath, mockFileData );
var manageFile = new ManageFile(mockFileSysteme);
Olivier Martial Soro
sumber
2

Anda dapat melakukannya dengan menggunakan Microsoft Fakes tanpa perlu mengubah basis kode Anda misalnya karena sudah dibekukan.

Pertama-tama hasilkan perakitan palsu untuk System.dll - atau paket lainnya dan kemudian mengejek pengembalian yang diharapkan seperti pada:

using Microsoft.QualityTools.Testing.Fakes;
...
using (ShimsContext.Create())
{
     System.IO.Fakes.ShimFile.ExistsString = (p) => true;
     System.IO.Fakes.ShimFile.ReadAllTextString = (p) => "your file content";

      //Your methods to test
}
Bahadır İsmail Aydın
sumber
1

Akan sulit untuk mengolok-olok sistem file dalam suatu pengujian karena .NET file API tidak benar-benar didasarkan pada antarmuka atau kelas yang dapat diperluas yang dapat diejek.

Namun, jika Anda memiliki lapisan fungsional Anda sendiri untuk mengakses sistem file, Anda bisa mengejeknya dalam unit test.

Sebagai alternatif untuk mengejek, pertimbangkan untuk hanya membuat folder dan file yang Anda butuhkan sebagai bagian dari pengaturan pengujian Anda, dan menghapusnya dalam metode teardown Anda.

LBushkin
sumber
1

Saya tidak yakin bagaimana Anda akan mengejek sistem file. Apa yang bisa Anda lakukan adalah menulis pengaturan fixture tes yang membuat folder, dll. Dengan struktur yang diperlukan untuk tes. Metode teardown akan membersihkannya setelah tes berjalan.

Diedit untuk menambahkan: Dalam memikirkan ini sedikit lagi, saya tidak berpikir Anda ingin mengejek sistem file untuk menguji metode jenis ini. Jika Anda mengejek sistem file untuk mengembalikan nilai true jika ada file tertentu dan menggunakannya dalam pengujian metode yang memeriksa apakah file itu ada, maka Anda tidak menguji apa-apa. Di mana mengolok-olok sistem file akan berguna adalah jika Anda ingin menguji metode yang memiliki ketergantungan pada sistem file tetapi aktivitas sistem file tidak terpisahkan dengan metode yang diuji.

Ide Jamie
sumber
1

Untuk menjawab pertanyaan spesifik Anda: Tidak, tidak ada perpustakaan yang akan memungkinkan Anda untuk mengejek panggilan file I / O (yang saya tahu). Ini berarti bahwa unit "yang benar" yang menguji tipe Anda akan mengharuskan Anda mempertimbangkan batasan ini saat menentukan tipe Anda.

Catatan samping cepat tentang cara saya mendefinisikan unit test "tepat". Saya percaya bahwa unit test harus mengkonfirmasi bahwa Anda mendapatkan output yang diharapkan (baik pengecualian, memanggil metode, dll) memberikan input yang dikenal. Ini memungkinkan Anda untuk mengatur kondisi pengujian unit Anda sebagai serangkaian input dan / atau status input. Cara terbaik yang saya temukan untuk melakukan ini adalah menggunakan layanan berbasis antarmuka dan injeksi ketergantungan sehingga setiap tanggung jawab eksternal untuk suatu tipe diberikan melalui antarmuka yang dilewatkan melalui konstruktor atau properti.

Jadi, dengan mengingat hal ini, kembalilah ke pertanyaan Anda. Saya telah mengejek panggilan sistem file dengan membuat IFileSystemServiceantarmuka bersama dengan FileSystemServiceimplementasi yang hanya fasad atas metode sistem file mscorlib. Kode saya kemudian menggunakan jenis IFileSystemServicedaripada mscorlib. Ini memungkinkan saya untuk memasukkan standar saya FileSystemServiceketika aplikasi sedang berjalan atau mengejek IFileSystemServicedalam unit test saya. Kode aplikasinya sama terlepas dari bagaimana dijalankannya, tetapi infrastruktur yang mendasarinya memungkinkan kode itu untuk dengan mudah diuji.

Saya akan mengakui bahwa itu adalah rasa sakit untuk menggunakan pembungkus di sekitar objek sistem file mscorlib tetapi, dalam skenario khusus ini, layak untuk bekerja ekstra karena pengujian menjadi jauh lebih mudah dan lebih dapat diandalkan.

akmad
sumber
1

Membuat antarmuka dan mengejeknya untuk pengujian adalah cara terbersih untuk dilakukan. Namun, sebagai alternatif, Anda dapat melihat kerangka Microsoft Moles .

Konamiman
sumber
0

Solusi umum adalah menggunakan beberapa API sistem file abstrak (seperti Apache Commons VFS untuk Java): semua logika aplikasi menggunakan API dan unit test dapat mengejek sistem file nyata dengan implementasi rintisan (emulasi dalam memori atau sesuatu seperti itu).

Untuk C # API serupa ada: NI.Vfs yang sangat mirip dengan Apache VFS V1. Ini berisi implementasi standar baik untuk sistem file lokal dan sistem file dalam memori (yang terakhir dapat digunakan dalam unit test dari kotak).

Vitaliy Fedorchenko
sumber
-1

Kami saat ini menggunakan mesin data berpemilik dan API-nya tidak diekspos sebagai antarmuka sehingga kami sulit menguji unit kode akses data kami. Lalu aku pergi dengan pendekatan Matt dan Joseph juga.

Tien Do
sumber
-2

Saya akan pergi dengan respons Jamie Ide. Jangan mencoba mengejek hal-hal yang tidak Anda tulis. Akan ada segala macam ketergantungan yang tidak Anda ketahui - kelas tertutup, metode non virtual dll.

Pendekatan lain adalah membungkus metode yang sesuai dengan sesuatu yang dapat dipermainkan. misalnya membuat kelas bernama FileWrapper yang memungkinkan akses ke metode File tetapi merupakan sesuatu yang dapat Anda tiru.

gbanfill
sumber