C # .NET statik bağımlılığı birim testi (sahte/saplama) için yalnızca 3 yolu olduğunu bulduk:
Bunlardan ikisinin ücretsiz olmadığı ve birinin sürüm 1.0'a çarpmadığı göz önüne alındığında, statik şeyleri alay etmek çok kolay değildir.
Bu statik yöntemler ve böyle bir "kötülük" yapar mı (birim test anlamında)? Ve eğer öyleyse, yeniden paylaşıcı neden statik, statik olabilecek herhangi bir şey yapmamı istiyor? (Yeniden paylaşıcının da "kötülük" olmadığı varsayılır.)
Açıklama: Bir yöntemi test etmek istediğinizde senaryodan bahsediyorum ve bu yöntem farklı birim/sınıf. Birim testlerin çoğu tanımına göre, test edilen yöntemin diğer birim/sınıftaki statik yöntemi çağırmasına izin verirseniz, birim testi değilsiniz birim testi, entegrasyon testi yapıyorsunuz. (Yararlı, ancak bir birim test değil.)
Buradaki diğer cevaplara baktığımda, statik durumu tutan veya yan etkilere neden olan (bana gerçekten kötü bir fikir gibi geliyor) statik yöntemler ve sadece bir değer döndüren statik yöntemler arasında bazı karışıklıklar olabileceğini düşünüyorum.
Hiçbir durumda olmayan ve hiçbir yan etkiye neden olmayan statik yöntemler kolayca birim test edilebilir olmalıdır. Aslında, bu tür yöntemleri fonksiyonel programlamanın "fakir bir adam" biçimi olarak görüyorum; yöntemi bir nesne veya değer verirseniz, bir nesne veya değer döndürür. Başka bir şey yok. Bu tür yöntemlerin birim testlerini nasıl olumsuz etkileyeceğini görmüyorum.
Statik veri ve statik yöntemler kafa karıştırıcı gibi görünüyor. Resharper, doğru hatırlıyorsam, bir sınıf içinde
private yönteminin statik hale getirilebilmesi durumunda yapılmasını önerir - bunun küçük bir performans avantajı sağladığını düşünüyorum. O yapmaz "olabilecek herhangi bir şey" statik yapmanızı öneririz!
Statik yöntemlerle ilgili yanlış bir şey yoktur ve test edilmesi kolaydır (statik verileri değiştirmedikleri sürece). Örneğin, statik yöntemlerle statik bir sınıfa iyi bir aday olan bir Matematik kütüphanesi düşünün. Bunun gibi (yapışık) bir yönteminiz varsa:
public static long Square(int x) { return x * x; }
o zaman bu kesinlikle test edilebilir ve hiçbir yan etkisi yoktur. Sadece geçtiğinizde mesela 20'yi geri aldığınızı kontrol edersiniz. 400. Sorun değil.
Buradaki asıl soru "Bu kodu nasıl test ederim?"
public class MyClass { public void MethodToTest() { //... do something MyStaticClass.StaticMethod(); //...more } }
Ardından, kodu yeniden düzenleyin ve her zamanki gibi statik sınıfa yapılan çağrıyı şu şekilde enjekte edin:
public class MyClass { private readonly IExecutor _externalExecutor; public MyClass(_IExecutor executor) { _exeternalExecutor = executor; } public void MethodToTest() { //... do something _exetrnalExecutor.DoWork(); //...more } } public class MyStaticClassExecutor : IExecutor { public void DoWork() { MyStaticClass.StaticMethod(); } }
Statiklerin kötü olması gerekmez, ancak sahte/alay/saplamalarla birim testi söz konusu olduğunda seçeneklerinizi sınırlayabilirler.
Alaycılığa iki genel yaklaşım vardır.
İlki (geleneksel - RhinoMocks, Moq, NMock2 tarafından uygulanır; manuel alaylar ve saplamalar da bu kampta bulunmaktadır) test dikişlerine ve bağımlılık enjeksiyonuna dayanır. Bazı statik kodları birim olarak test ettiğinizi ve bağımlılıkların olduğunu varsayalım. Bu şekilde tasarlanan kodda sık sık olan şey, statiklerin kendi bağımlılıklarını yaratarak bağımlılık tersini tersine çevirmesidir. Kısa süre sonra, bu şekilde tasarlanmış test edilen kod içine alaycı arayüzler enjekte edemeyeceğinizi keşfedeceksiniz.
İkincisi (alaycı bir şey - TypeMock, JustMock ve Moles tarafından uygulanır). NET'in Profiling API . CIL talimatlarınızdan herhangi birini kesebilir ve kodunuzun bir kısmını sahte ile değiştirebilir. Bu, TypeMock ve bu kamptaki diğer ürünlerin herhangi bir şeyi alay etmesine izin verir: statik, mühürlü sınıflar, özel yöntemler - test edilebilecek şekilde tasarlanmamış şeyler.
İki düşünce okulu arasında süregelen bir tartışma var. Biri diyor ki, SOLID ilkeleri ve test edilebilirlik için tasarım (genellikle statik olarak kolaylaşmayı içerir). Diğeri diyor ki, TypeMock satın alın ve endişelenmeyin.
Şuna bir bakın: "Statik Yöntemler Ölülebilirliğe Ölüm" . Argümanın kısa özeti:
Birim testi yapmak için kodunuzun küçük bir parçasını almanız, bağımlılıklarını yeniden kablolamanız ve tek tek test etmeniz gerekir. Statik yöntemlerle bu zordur, sadece küresel duruma erişmeleri durumunda değil, sadece diğer statik yöntemleri çağırsalar bile.
Nadiren kabul edilen basit gerçek, eğer bir sınıf başka bir sınıfa derleyici tarafından görülebilen bir bağımlılık içeriyorsa, o olamaz o sınıftan ayrı olarak test edilmelidir. Teste benzeyen ve raporda testmiş gibi görünecek bir şeyi taklit edebilirsiniz.
Ancak bir testin temel tanımlayıcı özelliklerine sahip olmayacaktır; işler yanlış olduğunda başarısız olur, doğru olduğunda geçer.
Bu, statik çağrılar, kurucu çağrıları ve temel sınıftan veya arabirimden devralınmayan yöntemlere veya alanlara yapılan başvurular için geçerlidir. Sınıf adı kodda görünüyorsa, derleyici tarafından görülebilen bir bağımlılıktır ve bu olmadan geçerli bir şekilde test edemezsiniz. Herhangi bir küçük yığın geçerli bir test edilebilir birim değildir. Herhangi bir işlemmiş gibi davranmaya yönelik herhangi bir girişim, test çerçeveniz tarafından kullanılan 'test geçti' demek için XML'i yayınlamak için küçük bir yardımcı program yazmaktan daha anlamlı sonuçlara sahip olmayacaktır.
Bununla birlikte, üç seçenek vardır:
bir sınıftan oluşan birimin ve sabit kodlu bağımlılıkların test edilmesi için birim testini tanımlar. Bu, dairesel bağımlılıklardan kaçınmanızı sağlar.
testten sorumlu olduğunuz sınıflar arasında asla derleme zamanı bağımlılıkları oluşturmayın. Bu, sonuçta ortaya çıkan kod stilini önemsememenizi sağlayarak çalışır.
birim testi yok, entegrasyon testi yerine. Hangi işe yarar, entegrasyon testi terimi için kullanmanız gereken başka bir şeyle çatışmaması koşuluyla çalışır.
Bunun iki yolu yok. ReSharper'ın önerileri ve C # 'ın birkaç yararlı özelliği, tüm kodunuz için izole edilmiş atomik birim testleri yazıyorsanız sık kullanılmaz.
Örneğin, statik bir yönteminiz varsa ve bunu saptamanız gerekiyorsa, profil tabanlı bir yalıtım çerçevesi kullanmadığınız sürece yapamazsınız. Çağrı uyumlu bir çözüm, lambda gösterimini kullanmak için yöntemin üst kısmını değiştirmektir. Örneğin:
ÖNCE:
public static DBConnection ConnectToDB( string dbName, string connectionInfo ) { }
SONRA:
public static Func<string, string, DBConnection> ConnectToDB (dbName, connectionInfo ) { };
İkisi çağrı uyumludur. Arayanların değişmesi gerekmez. Fonksiyonun gövdesi aynı kalır.
Daha sonra Unit-Test kodunuzda bu çağrıyı şu şekilde saplayabilirsiniz (Veritabanı adlı bir sınıfta olduğu varsayılarak):
Database.ConnectToDB = (dbName, connectionInfo) => { return null|whatever; }
İşlemi tamamladıktan sonra orijinal değerle değiştirmeye dikkat edin. Bunu bir deneme/son olarak veya birim test temizlemenizde, her testten sonra çağrılan bir kodla şöyle yapabilirsiniz:
[TestCleanup] public void Cleanup() { typeof(Database).TypeInitializer.Invoke(null, null); }
bu, sınıfınızın statik başlatıcısını yeniden başlatır.
Lambda Funcs, normal statik yöntemler kadar destek bakımından zengin değildir, bu nedenle bu yaklaşım aşağıdaki istenmeyen yan etkilere sahiptir:
Ancak diyelim ki statikten tamamen kaçındınız ve bunu bir örnek yöntemine dönüştürüyorsunuz. Yöntem sanal olmadığı veya bir arabirimin parçası olarak uygulanmadığı sürece hala taklit edilemez.
Gerçekte, statik yöntemleri saplamanın çaresini öneren herkes, bunları örnek yöntemleri yapmaktır, sanal olmayan veya bir arabirimin parçası olmayan örnek yöntemlerine karşı da olacaktır.
Peki neden C # 'ın statik yöntemleri var? Sanal olmayan örnek yöntemlerine neden izin veriyor?
Bu "Özelliklerden" birini kullanırsanız, izole edilmiş yöntemler oluşturamazsınız.
Peki onları ne zaman kullanıyorsun?
Bunları kimsenin saplamasını istemediğiniz herhangi bir kod için kullanın. Bazı örnekler: String sınıfının Format () yöntemi Console sınıfının WriteLine () yöntemi Math sınıfının Cosh () yöntemi
Ve bir şey daha .. Çoğu insan bunu umursamaz, ancak dolaylı bir çağrının performansı hakkında yapabiliyorsanız, örnek yöntemlerden kaçınmanın başka bir nedeni de budur. Bir performans vuruşu olduğu durumlar var. Bu yüzden ilk etapta sanal olmayan yöntemler var.
Uzun zaman sonra hiç kimsenin gerçekten basit bir gerçeği belirtmediğini görüyorum. Eğer yeniden paylaşıcı bana bir yöntemi statik yapabileceğimi söylüyorsa, bu benim için çok büyük bir şey ifade ediyor, sesini bana duyabiliyorum: "hey, sen, bu mantık parçaları şu anki sınıfın halletmek için SORUMLULUĞU değil, bu yüzden dışarıda kalmalı bazı yardımcı sınıflarda veya başka bir şeyde ".
R # bu öneriyi yapacak tek araç değildir. FxCop/MS Kod Analizi de aynısını yapacaktır.
Genelde yöntem statik ise, genellikle olduğu gibi test edilebilir olmalıdır. Bu, tasarım düşüncesini ve muhtemelen şu an parmaklarımdan daha fazla tartışmayı getiriyor, bu yüzden sabırla aşağı oyları ve yorumları bekliyor ...;)
Statik yöntem diğer yöntemin içinde olarak çağrılırsa, böyle bir çağrıyı önlemek veya değiştirmek mümkün değildir. Bu, bu iki yöntemin tek bir Birim oluşturduğu anlamına gelir; Her tür ünite testi ikisini de test eder.
Ve eğer bu statik yöntem Internet ile konuşursa, veritabanlarını bağlarsa, GUI açılır pencerelerini gösterirse veya Birim testini tam bir karmaşaya dönüştürürse, kolay bir iş yapmadan yapar. Bu tür statik yöntemi çağıran bir yöntem, birim test edilmesinden oldukça fayda sağlayacak çok fazla hesaplama koduna sahip olsa bile, yeniden düzenleme olmadan test edilemez.
Resharper'ın size rehberlik ettiğini ve kurduğu kodlama kurallarını uyguladığına inanıyorum. Resharper kullandığımda ve bana bir yöntemin statik olması gerektiğini söylediğinde, herhangi bir örnek değişkeni üzerinde işlem yapmayan özel bir yönteme bağlı olması gerekir.
Testablity'ye gelince, bu senaryo yine de özel yöntemleri test etmemeniz gerektiği için bir sorun olmamalıdır.
Herkese açık olan statik yöntemlerin test edilebilirliğine gelince, statik yöntemler statik duruma dokunduğunda birim testi zorlaşır. Şahsen bunu minimumda tutacağım ve bir test fikstürü ile kontrol edilebilen yönteme herhangi bir bağımlılığın geçtiği yerlerde mümkün olduğunca saf fonksiyonları statik yöntemler kullanacağım. Ancak bu bir tasarım kararıdır.