Test Edilebilir Kod Yazma ve Spekülatif Genelliği Önleme


11

Bu sabah bazı blog mesajları okurken, ve tökezledi bu bir :

Müşteri arabirimini uygulayan tek sınıf CustomerImpl ise, gerçekte polimorfizm ve ikame edilebilirliğiniz yoktur çünkü çalışma zamanında ikame edilecek hiçbir şey yoktur. Sahte genellik.

Bir arayüz uygulamak karmaşıklık eklediğinden ve yalnızca tek bir uygulama varsa, gereksiz karmaşıklık kattığı iddia edilebilir. Olması gerekenden daha soyut olan kod yazmak genellikle "spekülatif genellik" denilen kod kokusu olarak kabul edilir.

Ancak, TDD'yi takip edersem, sınıfsal kalıtım ve yöntemlerini sanal hale getiren arayüz uygulaması veya diğer polimorfik seçeneğimiz şeklinde olsun, bu spekülatif genellilik olmadan test çiftlerini (kolayca) oluşturamıyorum.

Peki, bu ödünleşmeyi nasıl uzlaştırıyoruz? Test / TDD'yi kolaylaştırmak için spekülatif olarak genel olmaya değer mi? Test çiftlerini kullanıyorsanız, bunlar ikinci uygulamalar olarak sayılır ve genelliği artık spekülatif yapmaz mı? Somut işbirlikçilerin alay edilmesine izin veren daha ağır bir alaycı çerçeve düşünmelisiniz (örneğin, C # dünyasında Moles'e karşı Moq)? Veya, somut sınıflarla test etmeli ve tasarımınız doğal olarak polimorfizm gerektirene kadar "entegrasyon" testleri olarak kabul edilebilecekleri yazmalı mısınız?

Diğer insanların bu konuyla ilgili aldıklarını okumayı merak ediyorum - görüşleriniz için şimdiden teşekkürler.


Şahsen ben varlıkların alay edilmemesi gerektiğini düşünüyorum. Ben sadece sahte hizmetler ve kod etki alanı kodu genellikle hizmetlerin uygulandığı koda referans yok, çünkü her durumda bir arabirim gerekir.
CodesInChaos

7
Dinamik olarak yazılan dillerin kullanıcıları, statik olarak yazılan dillerin size koyduğu zincirlerdeki şevlerinize gülüyoruz. Bu, dinamik olarak yazılan dillerde birim testini kolaylaştıran bir şeydir, test amacıyla bir nesneyi altyazılamak için bir arayüz geliştirmem gerekmiyor.
Winston Ewert

Arayüzler sadece genelliği etkilemek için kullanılmaz. Birçok amaç için kullanılır, kodunuzun ayrıştırılması önemli olanlardan biridir. Bu da kodunuzun test edilmesini çok daha kolay hale getirir.
Marjan Venema

@WinstonEwert Daha önce belirttiğiniz gibi, tipik olarak dinamik olarak yazılmış dillerde çalışmayan biri olarak düşünmediğim dinamik yazmanın ilginç bir yararı.
Erik Dietrich

@CodeInChaos Bu sorunun amaçları için ayrım yapmayı düşünmemiştim, ancak yapılması mantıklı bir ayrım. Bu durumda, sadece bir (mevcut) uygulama ile hizmet / çerçeve sınıflarını düşünebiliriz. Diyelim ki DAO'larla eriştiğim bir veritabanım var. İkincil bir kalıcılık modeli olana kadar DAO'larla arayüz oluşturmamalıyım? (Blog yazarı yazarının ima ettiği gibi görünüyor)
Erik Dietrich

Yanıtlar:


14

Blog gönderisini okudum ve okudum ve yazarın söylediklerinin çoğuna katılıyorum. Eğer birim test amaçlı arayüzleri kullanarak kodunuzu yazıyorsanız Ancak, ben arayüzünün sahte uygulama olduğunu söyleyebilirim olduğunu ikinci uygulama. Kodunuzun karmaşıklık yoluna gerçekten fazla bir şey katmadığını iddia ediyorum, özellikle de bunu yapmamanız, sınıflarınızın sıkı bir şekilde bağlanması ve test edilmesi zorlaşıyorsa.


3
Kesinlikle doğru. Test kodu olduğunu vb sağ tasarım, uygulama, bakım almak için gerektiğinden uygulamanın parçası. Eğer Müşteriye gönderim yok olması önemsizdir - test paketi ikinci uygulama varsa, o zaman genelliği olan var ve bunu karşılamak gerekir.
Kilian Foth

1
Bu en inandırıcı bulduğum cevap (ve @KilianFoth kod ikinci bir uygulama gemi olsun ya da olmasın ekleyerek hala var). Yine de, başka birinin girip girmediğini görmek için cevabı biraz kabul etmeye devam edeceğim.
Erik Dietrich

Ayrıca, testlerdeki arayüzlere bağlı olduğunuzda, genelliğin artık spekülatif olmadığını da ekleyeceğim
Pete

"Bunu yapmamak" (arayüzler kullanarak) otomatik olarak sınıflarınızın sıkıca bağlanmasıyla sonuçlanmaz. Sadece öyle değil. Örneğin, .NET Framework'te bir Streamsınıf vardır, ancak sıkı bağlantı yoktur.
Luke Puplett

3

Kodu genel olarak test etmek kolay değildir. Öyle olsaydı, bunu uzun zaman önce yapardık ve sadece son 10-15 yılda böyle bir şey yapmazdık. En büyük zorluklardan biri, her zaman kapsülleme kırılmadan uyumlu, iyi faktörlü ve test edilebilir kodların nasıl test edileceğinin belirlenmesidir. BDD müdürü neredeyse tamamen davranışa odaklanmamızı önermektedir ve bazı açılardan iç detaylar hakkında bu kadar büyük bir endişe duymanız gerekmediğini düşündürmektedir, ancak bu genellikle test edilmesi oldukça zor olabilir. daha gizli bir şekilde tüm olası sonuçlarla başa çıkmak için testinizin genel karmaşıklığını artırabileceğinden, çok gizli bir şekilde "şeyler" yapan birçok özel yöntem.

Alay etmek belirli bir dereceye kadar yardımcı olabilir, ancak yine de oldukça dışarıdan odaklanmıştır. Bağımlılık Enjeksiyonu da yine alay veya test ikilileri ile oldukça güzel çalışabilir, ancak bu aynı zamanda öğeleri bir arabirim yoluyla veya doğrudan gizli kalmayı tercih edebileceğiniz öğeleri ortaya çıkarmanızı gerektirebilir - bu özellikle isterseniz doğrudur sisteminizdeki belirli sınıflar hakkında iyi bir paranoyak güvenlik seviyesine sahip olun.

Benim için jüri, sınıflarınızı daha kolay test edilebilir olacak şekilde tasarlayıp tasarlamayacağınıza hala karar verdi. Bu, eski kodu korurken yeni testler sağlamanız gerektiğini fark ederseniz sorun yaratabilir. Bir sistemdeki her şeyi kesinlikle test edebilmenizi kabul ediyorum, ancak onlar için bir test yazabilmem için bir sınıfın özel içsellerini - dolaylı olarak bile - ortaya çıkarma fikrinden hoşlanmıyorum.

Benim için çözüm her zaman oldukça pragmatik bir yaklaşım benimsemek ve her bir özel duruma uyacak bir dizi tekniği birleştirmek olmuştur. Testlerim için iç özellikleri ve davranışları ortaya çıkarmak için çok sayıda kalıtsal test ikilisi kullanıyorum. Sınıflarıma iliştirilebilecek her şeyi alay ediyorum ve sınıflarımın güvenliğinden ödün vermeyeceği yerlerde, test amacıyla davranışları geçersiz kılmak veya enjekte etmek için bir araç sunacağım. Kod test etme yeteneğini geliştirmeye yardımcı olacaksa, daha fazla olay odaklı bir arayüz sağlamayı düşüneceğim.

Ben herhangi bir "test edilemez" kodu bulmak nerede , ben şeyler daha test edilebilir hale getirmek için refactor olup olmadığını görmek için bakmak. Sahnelerin arkasına gizlenmiş çok sayıda özel kodunuz olduğunda, genellikle dağılmayı bekleyen yeni sınıflar bulacaksınız. Bu sınıflar dahili olarak kullanılabilir, ancak genellikle daha az özel davranışla ve daha sonra genellikle daha az erişim ve karmaşıklık katmanlarıyla bağımsız olarak test edilebilir. Kaçınılması gereken bir şey, ancak test kodu yerleşik olarak üretim kodu yazmaktır. Bu tür dehşet dahil sonuçlanan " test pabuçları " oluşturmak cazip olabilir if testing then ..., hangi bir test sorunu tamamen çözülmüş ve eksik çözülmüş değil gösterir.

Gerard Meszaros'un xUnit Test Patterns kitabını okumakta fayda var. Muhtemelen önerdiği her şeyi yapmıyorum, ancak başa çıkmak için bazı daha zorlu test durumlarını netleştirmeye yardımcı oluyor. Günün sonunda, tercih ettiğiniz tasarımları uygularken test gereksinimlerinizi karşılayabilmek istersiniz ve nerede ödün vermeniz gerektiğine daha iyi karar verebilmek için tüm seçenekleri daha iyi anlamanıza yardımcı olur.


1

Kullandığınız dilin test için bir nesneyi "taklit etmenin" bir yolu var mı? Eğer öyleyse bu can sıkıcı arayüzler ortadan kalkabilir.

Farklı bir notta, bunu uygulayan bir SimpleInterface ve tek bir ComplexThing'e sahip olmanız için nedenler olabilir. SimpleInterface kullanıcısı tarafından erişilebilir olmasını istemediğiniz ComplexThing parçaları olabilir. Her zaman aşırı coşkulu bir OO-ish kodlayıcı nedeniyle değildir.

Şimdi uzaklaşacağım ve herkesin bunu yapan kodun onlara "kötü kokuyor" gerçeğini atlamasına izin vereceğim.


Evet, alaycı somut nesneleri destekleyen alaycı çerçevelerle dil (ler) de çalışıyorum. Bu araçlar yapmak için çemberlerden çeşitli derecelerde atlama gerektirir.
Erik Dietrich

0

İki kısımda cevap vereceğim:

  1. Yalnızca test etmekle ilgileniyorsanız arayüzlere ihtiyacınız yoktur. Bu amaçla alaycı çerçeveler kullanıyorum (Java'da: Mockito veya easymock). Tasarladığınız kodun test amacıyla değiştirilmemesi gerektiğine inanıyorum. Test edilebilir kod yazma, modüler kod yazma eşdeğerdir, bu yüzden modüler (test edilebilir) kod yazma ve sadece kod ortak arayüzleri test eğilimindedir.

  2. Ben büyük bir Java projesinde çalışıyoruz ve derinden salt okunur (sadece getters) arayüzlerini kullanarak olduğuna ikna olma ediyorum (I değişmezlik büyük bir hayranı olduğumu lütfen not) gitmek için yol. Uygulayıcı sınıfın ayarlayıcıları olabilir, ancak bu dış katmanlara maruz bırakılmaması gereken bir uygulama detayıdır. Başka bir açıdan bakıldığında kompozisyonu kalıtım yerine (modülerlik, hatırla? Kendimi ayağımdan vurmaktan ziyade spekülatif cömertliğin bedelini ödemeye hazırım .


0

Ben polimorfizm ötesinde bir arayüze daha fazla programlama başladığımdan beri birçok avantaj gördüm.

  • Beni önceden sınıfın arabirimi (genel yöntemler) ve diğer sınıfların arayüzleriyle nasıl etkileşime gireceği hakkında daha fazla düşünmeye zorluyor.
  • Daha uyumlu ve tek sorumluluk ilkesini takip eden daha küçük sınıflar yazmama yardımcı oluyor.
  • Kodumu test etmek daha kolay
  • Sınıf örnek düzeyinde olması gerektiğinden daha az statik sınıf / genel durum
  • Tüm program hazır olmadan parçaları birleştirmek ve birleştirmek daha kolay
  • Bağımlılık enjeksiyonu, nesne yapısını iş mantığından ayıran

Birçok insan, daha küçük sınıfların daha az, daha büyük sınıflardan daha iyi olduğu konusunda hemfikir olacaktır. Bir kerede fazla odaklanmanıza gerek yoktur ve her sınıfın iyi tanımlanmış bir amacı vardır. Diğerleri ise daha fazla sınıfa sahip olarak karmaşıklığa katkıda bulunduğunuzu söyleyebilir.

Verimliliği artırmak için araçlar kullanmak iyidir, ancak bence sadece Mock çerçevelerine güvenmek ve test edilebilirlik ve modülerliği doğrudan koda dönüştürmek yerine uzun vadede daha düşük kalitede kodla sonuçlanacaktır.

Sonuçta, daha kaliteli kod yazmama yardımcı olduğuna inanıyorum ve faydaları herhangi bir sonuçtan ağır basar.

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.