Statik olarak yazılan fonksiyonel kodun birim testi


15

Sana insanlara sormak istedim, bu durumda, haskell, scala, ocaml, nemerle, f # veya haXe'de yazıldığı gibi statik olarak test edilen fonksiyonel kodu birim test etmek mantıklıdır (son olarak gerçekten ilgilendiğim şey, ama istedim daha büyük toplulukların bilgisinden faydalanabilirsiniz).

Bunu soruyorum çünkü anlayışımdan:

  • Birim testlerin bir özelliği spesifikasyonların çalıştırılabilir formda olmasıdır. Ancak, biçimsel özellikleri doğrudan dil anlambilimiyle eşleştiren bildirici bir stil kullanılırken , spesifikasyonları çalıştırılabilir formda ayrı bir şekilde ifade etmek bile mümkün , bu değer katıyor mu?

  • Birim testlerin daha belirgin yönü, statik analiz yoluyla ortaya çıkarılamayan hataları takip etmektir. Tip güvenli fonksiyonel kodun, statik analizörünüzün anladığına çok yakın kodlamak için iyi bir araç olduğu göz önüne alındığında, çok fazla güvenliği statik analize doğru kaydırabilirsiniz. Ancak kodunuzda (her ikisi de koordinat olmak xyerine) kullanmak gibi basit bir hata yele alınamaz. Test kodu yazarken OTOH böyle bir hata da ortaya çıkabilir, bu yüzden çabaya değip değmeyeceğinden emin değilim.

  • Birim testleri artıklık getirir, yani gereksinimler değiştiğinde bunları uygulayan kod ve bu kodu kapsayan testlerin de değiştirilmesi gerekir. Elbette bu yük sabittir, bu yüzden gerçekten önemli olmadığı iddia edilebilir. Aslında, Ruby gibi dillerde gerçekten faydalarla karşılaştırılamaz, ancak statik olarak tipik fonksiyonel programlamanın zemin ünitesi testlerinin birçoğunu nasıl kapsadığı düşünüldüğünde, cezalandırılmadan basit bir şekilde azaltılabilen sabit bir ek yük gibi hissedilir.

Bundan, ünite testlerinin bu programlama tarzında biraz eskimiş olduğunu söyleyebilirim. Elbette böyle bir iddia sadece dini savaşlara yol açabilir, bu yüzden bunu basit bir soruya kaynatmama izin verin:

Böyle bir programlama stili kullandığınızda, birim testlerini hangi kapsamda kullanıyorsunuz ve neden (kodunuz için hangi kaliteyi kazanmayı umuyorsunuz)? Ya da başka bir yolla: statik analizör tarafından kapsanan ve dolayısıyla birim test kapsamına ihtiyaç duymadan, statik olarak tiplendirilmiş bir fonksiyonel kod birimini niteleyebileceğiniz kriterleriniz var mı?


4
Bu arada, QuickCheck'i denemediyseniz , kesinlikle yapmalısınız.
Jon Purdy

scalacheck.org Scala eşdeğeri
V-Lamp

Yanıtlar:


8

Birim testlerin bir özelliği spesifikasyonların çalıştırılabilir formda olmasıdır. Ancak, biçimsel özellikleri doğrudan dil anlambilimiyle eşleştiren bildirici bir stil kullanılırken, spesifikasyonları çalıştırılabilir formda ayrı bir şekilde ifade etmek bile mümkün, bu değer katıyor mu?

İşlev bildirimlerine doğrudan eşleştirilebilen spesifikasyonlarınız varsa - iyi. Ancak bunlar tipik olarak tamamen farklı iki soyutlama seviyesidir. Birim testleri, işlev üzerinde çalışan aynı geliştirici tarafından beyaz kutu testleri olarak yazılan tek kod parçalarını test etmeyi amaçlamaktadır. Özellikler normalde "Bu değeri buraya girip bu düğmeye bastığımda, bu ve bu olmalı" gibi görünüyor. Tipik olarak böyle bir özellik, birden fazla fonksiyonun geliştirilmesine ve test edilmesine yol açar.

Ancak, kodunuzda y yerine (her ikisi de koordinattır) x kullanmak gibi basit bir hata ele alınamaz. Ancak böyle bir hata test kodunu yazarken de ortaya çıkabilir, bu yüzden çabaya değip değmeyeceğinden emin değilim.

Yanlış anlaşılmanız, birim testlerinin aslında kodunuzdaki hataları ilk elden bulmaya yönelik olduğudur - bu doğru değildir, en azından sadece kısmen doğrudur. Kodunuz geliştikten sonra hataları girmenizi önlemek için yapılırlar. Bu nedenle, işlevinizi ilk kez test ettiğinizde ve birim testiniz çalıştığında ("x" ve "y" düzgün şekilde yerinde) ve sonra yeniden düzenleme yaparken y yerine x kullanırsınız, o zaman birim testi bunu size gösterecektir.

Birim testleri artıklık getirir, yani gereksinimler değiştiğinde bunları uygulayan kod ve bu kodu kapsayan testlerin de değiştirilmesi gerekir. Tabii ki bu yük sabittir, bu yüzden gerçekten önemli olmadığı iddia edilebilir. Aslında, Ruby gibi dillerde gerçekten faydalarla karşılaştırılamaz, ancak statik olarak tipik fonksiyonel programlamanın zemin ünitesi testlerinin birçoğunu nasıl kapsadığı düşünüldüğünde, cezalandırılmadan basit bir şekilde azaltılabilen sabit bir ek yük gibi hissedilir.

Mühendislikte, çoğu güvenlik sistemi yedekliliğe dayanır. Örneğin, bir arabada iki mola, bir gökyüzü dalgıcı için yedek paraşüt vb. Aynı fikir birim testleri için de geçerlidir. Tabii ki, gereksinimler değiştiğinde değiştirmek için daha fazla koda sahip olmak bir dezavantaj olabilir. Bu nedenle, özellikle birim testlerde onları KURU tutmak önemlidir ("Kendinizi Tekrarlama" ilkesine uyun). Statik olarak yazılan bir dilde, zayıf yazılan bir dilde olduğundan daha az birim testleri yazmak zorunda kalabilirsiniz. Özellikle "resmi" testler gerekli olmayabilir - bu iyi bir şeydir, çünkü önemli şeyleri test eden önemli birim testleri üzerinde çalışmak için daha fazla zaman verir. Ve sadece statik tipleriniz olduğundan, birim testlere ihtiyacınız olmadığından düşünmeyin, yeniden düzenleme sırasında hataların ortaya çıkması için hala çok yer var.


5

Birim testlerin bir özelliği spesifikasyonların çalıştırılabilir formda olmasıdır. Ancak, biçimsel özellikleri doğrudan dil anlambilimiyle eşleştiren bildirici bir stil kullanılırken, spesifikasyonları çalıştırılabilir formda ayrı bir şekilde ifade etmek bile mümkün, bu değer katıyor mu?

Spesifikasyonlarınızı tip kısıtlamaları olarak tamamen ifade edebilmeniz pek olası değildir .

Böyle bir programlama stili kullandığınızda, birim testlerini hangi kapsamda kullanıyorsunuz ve neden (kodunuz için hangi kaliteyi kazanmayı umuyorsunuz)?

Aslında, bu stilin en büyük yararı, saf işlevlerin birim testi daha kolay olmasıdır: harici durumu ayarlamaya veya yürütmeden sonra kontrol etmeye gerek yoktur.

Genellikle bir işlevin belirtimi (veya bir kısmı), argümanlara döndürülen değeri ilişkilendiren özellikler olarak ifade edilebilir . Bu durumda QuickCheck (Haskell için) veya ScalaCheck (Scala için) kullanmak, bu özellikleri dilin ifadeleri olarak yazmanıza ve rastgele girişler için tutulduklarını kontrol etmenize izin verebilir.


1
QuickCheck hakkında biraz daha ayrıntılı: temel fikir, "özellikler" (kodunuzdaki değişmezler) yazmanız ve potansiyel girdinin nasıl oluşturulacağını belirlemenizdir. Quickcheck daha sonra bir ton rastgele giriş oluşturur ve değişmezinizin her durumda tutmasını sağlar. Birim testinden daha kapsamlıdır.
Tikhon Jelvis

1

Ünite testlerini, kodun nasıl kullanılacağına dair bir örnek olarak ve neden değerli olduğunun bir açıklaması olarak düşünebilirsiniz.

İşte Bob Amca'nın John Conway'in "Hayat Oyunu" nda benimle eşleşecek kadar nazik olduğu bir örnek . Bu tür şeyler için mükemmel bir egzersiz olduğunu düşünüyorum. Testlerin çoğu tam sistemdir, tüm oyunu test eder, ancak ilk olarak sadece bir işlevi test eder - bir hücrenin etrafındaki komşuları hesaplayan. Tüm testlerin açıklayıcı bir şekilde yazıldığını, aradığımız davranış açıkça dile getirildiğini görebilirsiniz.

Fonksiyonlarda kullanılan işlevlerle alay etmek de mümkündür; ya fonksiyona geçirerek (bağımlılık enjeksiyonuna eşdeğer) ya da Brian Marick'in Midje gibi çerçevelerle .


0

Evet, ünite testleri statik olarak yazılan fonksiyonel kodla zaten anlamlıdır. Basit bir örnek:

prop_encode a = (decode . encode $ a) == a

prop_encodeStatik tip ile zorlayabilirsiniz .

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.