Birim testi için C # 'da dosya sistemini nasıl taklit edersiniz?


153

Birim testleri yazmak için C # 'da dosya sistemini taklit edecek kitaplıklar veya yöntemler var mı? Mevcut durumumda, belirli bir dosyanın var olup olmadığını kontrol eden ve oluşturma tarihini okuyan yöntemlerim var. Bundan daha fazlasına ihtiyacım olabilir.


1
Bu, aşağıdakiler de dahil olmak üzere diğer birkaçının kopyası gibi görünüyor: stackoverflow.com/questions/664277/… .
John Saunders

Belki pex'e bakmayı deneyebilirsiniz ( research.microsoft.com/en-us/projects/pex/filesystem.pdf )
Tinus

2
@Mitch: Çoğu zaman, verileri dosya sistemine yerleştirmek ve birim testlerinin kendi yollarını çalıştırmasına izin vermek yeterlidir. Bununla birlikte, birçok IO işlemini yürüten yöntemlerle karşılaştım ve bu tür yöntemler için test ortamını kurmak, sahte bir dosya sistemi kullanılarak büyük ölçüde basitleştirildi.
Steve Guidi

Bu amaçla github.com/gu Guillaume86/VirtualPath yazdım (ve daha fazlası), hala WIP ve API kesinlikle değişecek ama zaten çalışıyor ve bazı testler dahil.
Guillaume86

Yanıtlar:


159

Düzenleme: NuGet paketini yükleyin System.IO.Abstractions.

Bu yanıt ilk kabul edildiğinde bu paket yoktu. Orijinal cevap, aşağıda tarihsel bağlam için verilmiştir:

Bunu bir arayüz oluşturarak yapabilirsiniz:

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

ve System.IO.File.Exists () vb. kullanan 'gerçek' bir uygulama yaratma. Daha sonra bu arayüzle bir alay çerçevesi kullanarak alay edebilirsiniz; Moq'u tavsiye ederim .

Düzenleme: Biri bunu yaptı ve nazikçe burada çevrimiçi yayınladı .

Bu yaklaşımı, DateTime.UtcNow'u bir IClock arayüzünde (zamanın akışını kontrol edebilmek için testimiz için gerçekten yararlı!) Ve daha geleneksel olarak bir ISqlDataAccess arayüzünde alay etmek için kullandım.

Diğer bir yaklaşım TypeMock kullanmak olabilir , bu, sınıflara gelen çağrıları yakalamanıza ve onları dışarıda bırakmanıza izin verir. Ancak bu maliyetlidir ve çalışması için tüm ekibinizin bilgisayarlarına ve derleme sunucunuza yüklenmesi gerekir, ayrıca, mscorlib'i saplayamayacağı için System.IO.File için de çalışmayacaktır .

Ayrıca belirli yöntemlerin birim test edilebilir olmadığını kabul edebilir ve bunları yavaş çalışan ayrı bir entegrasyon / sistem testleri paketinde test edebilirsiniz.


1
Bana göre, Matt'in burada anlattığı gibi bir arayüz oluşturmak, gidilecek yoldur. Sizin için bu tür arayüzler oluşturan bir araç bile yazdım, bu, statik ve / veya mühürlenmiş sınıflarla veya deterministik olmayan yöntemlerle (yani saatler ve rastgele sayı üreteçleri) alay etmeye çalışırken yararlıdır. Daha fazla bilgi için jolt.codeplex.com adresini ziyaret edin.
Steve Guidi

Görünüşe göre atıfta bulunulan makaledeki depo önceden haber verilmeksizin silinmiş / taşınmış. Bununla birlikte, burada çabalarının bir nuget paketi var gibi görünüyor: nuget.org/packages/mscorlib-mock
Mike-E

Typemock'un hangi türlerin sahte olduğu konusunda kısıtlamaları vardır, ancak (en azından Ekim 2017'den itibaren geçerli sürümde) kesinlikle File statik sınıfını taklit edebilirsiniz. Bunu kendim doğruladım.
Ryan Rodemoyer

Bazı entegrasyon test paketlerini özetleyebilir misiniz?
Özkan

83

Kurulum Paketi System.IO.Abstractions

Bu sanal kitaplık artık var, System.IO ad alanını soyutlayan System.IO.Abstractions için bir NuGet paketi var .

Ayrıca bir dizi test yardımcıları da vardır, System.IO.Abstractions.TestingHelpers - bu yazma sırasında - sadece kısmen uygulanmıştır, ancak çok iyi bir başlangıç ​​noktasıdır.


3
Zaten inşa edilmiş olan bu soyutlama etrafında standartlaştırmanın en iyi seçenek olduğunu düşünüyorum. O kütüphaneyi hiç duymadım, bu yüzden uyarılar için çok teşekkürler.
julealgon

PM, paket yöneticisinin kısaltmasıdır .. açmak için ... Araçlar> NuGet Paket Yöneticisi> Paket Yöneticisi Konsolu
thedanotto

11

Dosya sisteminden neye ihtiyacınız olduğunu tanımlamak için muhtemelen bir sözleşme yapmanız ve ardından bu işlevlerin etrafına bir sarmalayıcı yazmanız gerekecek. Bu noktada, uygulamayla alay edebilir veya uygulayabilirsiniz.

Misal:

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"); }
}

5

Benim tavsiyem, Sistem ad alanında en çok kullanılan türler için sarmalayıcılar sağladığı için http://systemwrapper.codeplex.com/ kullanmaktır.


Şu anda bu kitaplığı kullanıyorum ve FileStream gibi şeyler için soyutlamalarının IDisposable içermediğini öğrendiğime göre, bir yedek arıyorum. Kitaplık, akışları düzgün bir şekilde imha etmeme izin vermiyorsa, bu tür işlemlerin üstesinden gelmek için onu öneremem (veya kullanamam).
James Nail

1
SystemWrapper'ın IFileStreamWrap'i artık IDisposable'ı uyguluyor.
tster

systemwrapper yalnızca .net çerçevesidir, .netcore ile kullanılırsa garip sorunlara neden olur
Adil H. Raza

3

Bunun için aşağıdaki çözümlerle karşılaştım:

  • Birim testleri değil, Entegrasyon testleri yazın. Bunun işe yaraması için, diğer testlerin karışması konusunda endişelenmeden bir şeyler atabileceğiniz bir klasör oluşturmanın basit bir yoluna ihtiyacınız var. Kullanmak için test yöntemi başına benzersiz bir klasör oluşturabilen basit bir TestFolder sınıfım var.
  • Alay edilebilir bir System.IO.File yazın. Yani bir IFile.cs oluşturun . Bunu kullanmanın genellikle alaycı ifadeler yazabileceğinizi kanıtlayan ancak IO kullanımı küçük olduğunda bunu kullanan testlerle sonuçlandığını görüyorum.
  • Soyutlama katmanınızı inceleyin ve IO dosyasını sınıftan çıkarın. Bunun için bir arayüz oluşturun. Geri kalanlar entegrasyon testlerini kullanır (ancak bu çok küçük olacaktır). Bu, dosya yapmak yerine yukarıdakinden farklıdır. İoThingie.loadSettings () diyelim ki amacı yazdığınızı okuyun.
  • System.IO.Abstractions . Bunu henüz kullanmadım, ama oynamaktan en çok heyecan duyduğum şey bu.

Yazdığım şeye bağlı olarak yukarıdaki tüm yöntemleri kullanıyorum. Ama çoğu zaman IO'ya denk gelen birim testleri yazdığımda soyutlamanın yanlış olduğunu düşünmeye başladım.


4
IFile.cs bağlantısı kesildi.
Mike-E

3

System.IO.Abstractions ve System.IO.Abstractions.TestingHelpers kullanarak böyle:

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;
   }
}

Test Sınıfınızda, dosyayla alay etmek için MockFileSystem () kullanırsınız ve ManageFile'ı şu şekilde tutarsınız:

var mockFileSysteme = new MockFileSystem();
var mockFileData = new MockFileData("File content");
mockFileSysteme.AddFile(mockFilePath, mockFileData );
var manageFile = new ManageFile(mockFileSysteme);

2

Bunu, örneğin zaten donmuş olduğu için kod tabanınızı değiştirmenize gerek kalmadan Microsoft Fakes kullanarak yapabilirsiniz .

Önce System.dll veya başka bir paket için sahte bir derleme oluşturun ve ardından aşağıdaki gibi beklenen getirileri taklit edin:

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
}

1

.NET dosya API'leri gerçekte alay edilebilecek arayüzlere veya genişletilebilir sınıflara dayalı olmadığından, bir testte dosya sistemiyle dalga geçmek zor olurdu.

Ancak, dosya sistemine erişmek için kendi işlevsel katmanınız varsa, bununla bir birim testinde dalga geçebilirsiniz.

Alay etmeye alternatif olarak, test kurulumunuzun bir parçası olarak ihtiyacınız olan klasör ve dosyaları oluşturmayı ve bunları sökme yönteminizde silmeyi düşünün.


1

Dosya sistemini nasıl modelleyeceğinizden emin değilim. Yapabileceğiniz şey, testler için gerekli yapıya sahip bir klasör vb. Oluşturan bir test fikstürü kurulumu yazmaktır. Bir sökme yöntemi, testler çalıştırıldıktan sonra onu temizler.

Eklemek için düzenlendi: Bunu biraz daha düşünürken, bu tür yöntemleri test etmek için dosya sistemiyle dalga geçmek isteyeceğinizi sanmıyorum. Belirli bir dosya varsa doğru sonucunu döndürmek için dosya sistemiyle dalga geçerseniz ve o dosyanın var olup olmadığını kontrol eden bir yöntemi test ederken bunu kullanırsanız, o zaman pek bir şeyi test etmiyorsunuz demektir. Dosya sistemi ile alay etmenin yararlı olacağı yerde, dosya sistemine bağımlı olan ancak dosya sistemi etkinliği test edilen yöntemin ayrılmaz bir parçası olmayan bir yöntemi test etmek istemenizdir.


1

Özel sorunuzu cevaplamak için: Hayır, dosya G / Ç çağrılarıyla (benim bildiğim) alay etmenize izin verecek kitaplık yok. Bu, türlerinizin "düzgün" birim testlerinin türlerinizi tanımlarken bu kısıtlamayı dikkate almanızı gerektireceği anlamına gelir.

"Uygun" bir birim testini nasıl tanımladığımla ilgili kısa yan not. Birim testlerinin, bilinen girdiler sağlanan beklenen çıktıyı (bir istisna, bir yöntem çağrısı vb.) Aldığınızı doğrulaması gerektiğine inanıyorum. Bu, birim test koşullarınızı bir dizi giriş ve / veya giriş durumu olarak ayarlamanıza olanak tanır. Bunu yapmanın en iyi yolu, arayüz tabanlı hizmetler ve bağımlılık enjeksiyonu kullanmaktır, böylece bir türe ait her bir sorumluluk, bir yapıcı veya özellik aracılığıyla iletilen bir arabirim aracılığıyla sağlanır.

Yani, bunu akılda tutarak, sorunuza geri dönelim. Mscorlib dosya sistemi yöntemleri üzerinde basitçe bir cephe IFileSystemServiceolan bir FileSystemServiceuygulama ile birlikte bir arayüz oluşturarak dosya sistemi çağrılarıyla dalga geçtim . IFileSystemServiceKodum daha sonra mscorlib türleri yerine kullanıyor . Bu FileSystemService, uygulama çalışırken benim standardımı takmama veya IFileSystemServicebirim testlerimde alay etmeme izin veriyor . Uygulama kodu, nasıl çalıştırıldığına bakılmaksızın aynıdır, ancak temel altyapı bu kodun kolayca test edilmesini sağlar.

Mscorlib dosya sistemi nesneleri etrafında sarmalayıcı kullanmanın bir acı olduğunu kabul edeceğim, ancak bu özel senaryolarda, test çok daha kolay ve daha güvenilir hale geldiğinden ekstra çalışmaya değer.


1

Bir arayüz oluşturmak ve test için onunla dalga geçmek, gitmenin en temiz yoludur. Bununla birlikte, alternatif olarak Microsoft Moles çerçevesine bir göz atabilirsiniz .


0

Yaygın çözüm, bazı soyut dosya sistemi API'leri kullanmaktır ( Java için Apache Commons VFS gibi ): tüm uygulama mantığı API kullanır ve birim testi, saplama uygulamasıyla (bellek içi öykünme veya buna benzer bir şey) gerçek dosya sistemini taklit edebilir.

C # için benzer API mevcuttur: Apache VFS V1'e çok benzeyen NI.Vfs . Hem yerel dosya sistemi hem de bellek içi dosya sistemi için varsayılan uygulamaları içerir (sonuncusu kutudan birim testlerinde kullanılabilir).


-1

Şu anda tescilli bir veri motoru kullanıyoruz ve API'sı arayüz olarak açığa çıkmadığından, veri erişim kodumuzu neredeyse birim test edemiyoruz. Sonra Matt ve Joseph'in yaklaşımıyla da gittim.


-2

Jamie Ide'nin cevabıyla giderdim. Yazmadığınız şeyleri alay etmeye çalışma. Bilmediğiniz her türden bağımlılık olacaktır - kapalı sınıflar, sanal olmayan yöntemler vb.

Başka bir yaklaşım, uygun yöntemleri alay edilebilir bir şeyle sarmalamak olacaktır. örneğin, File yöntemlerine erişime izin veren, ancak alay edebileceğiniz bir şey olan FileWrapper adlı bir sınıf oluşturun.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.