Durum bilgisi olan bir sistem için birim testleri tasarlama


20

Arka fon

Test Odaklı Geliştirme , okulu bitirdikten sonra ve endüstride popüler hale geldi. Öğrenmeye çalışıyorum, ancak bazı önemli şeyler hala benden kaçıyor. TDD taraftarları aşağıdakiler gibi birçok şey söylüyor (bundan böyle "tek iddia ilkesi" veya SAP olarak anılacaktır ):

Bir süredir TDD testlerinin nasıl olabildiğince basit, etkileyici ve zarif olabileceğini düşünüyorum. Bu makale, testleri olabildiğince basit ve ayrıştırmanın nasıl bir şey olduğunu araştırıyor: her testte tek bir iddiayı hedeflemek.

Kaynak: http://www.artima.com/weblogs/viewpost.jsp?thread=35578

Ayrıca şöyle şeyler söylerler (bundan böyle "özel yöntem ilkesi" veya PMP olarak anılacaktır ):

Özel yöntemleri doğrudan doğrudan birim test yapmazsınız. Özel olduklarından, onları bir uygulama detayı olarak düşünün. Hiç kimse bunlardan birini aramayacak ve belirli bir şekilde çalışmasını beklemeyecek.

Bunun yerine genel arayüzünüzü test etmelisiniz. Özel yöntemlerinizi çağıran yöntemler beklediğiniz gibi çalışıyorsa, uzantı yoluyla özel yöntemlerinizin doğru çalıştığını varsayarsınız.

Kaynak: Özel yöntemleri nasıl test ediyorsunuz?

Durum

Durum bilgisi olan bir veri işleme sistemini test etmeye çalışıyorum. Sistem, bu verileri almadan önce ne olduğu göz önüne alındığında, aynı veri parçası için farklı şeyler yapabilir. Sistemde durumu oluşturan basit bir testi düşünün, sonra verilen yöntemin test etmeyi amaçladığı davranışı test edin.

  • SAP ben "devlet oluşturma prosedürü" test olmamalıdır, devletin inşa kodundan beklediğim olduğunu ve daha sonra test etmeye çalışıyorum bir devlet değişikliği test varsaymak gerektiğini önerir

  • PMP, bu "durum oluşturma" adımını atlayamayacağımı ve bu işlevselliği bağımsız olarak yöneten yöntemleri test edemediğimi gösteriyor.

Gerçek kodumun sonucu şişkin, karmaşık, uzun ve yazılması zor testler oldu. Durum geçişleri değişirse, testlerin değiştirilmesi gerekir ... bu küçük, verimli testlerle iyi olur, ancak bu uzun şişirilmiş testlerle son derece zaman alıcı ve kafa karıştırıcı olur. Bu normalde nasıl yapılır?


2
Bunun için zarif bir çözüm bulacağınızı sanmıyorum. Genel yaklaşım, sistemi başlamak için durumlu hale getirmektir, bu da zaten inşa edilmiş bir şeyi test ederken size yardımcı olmaz. Vatansız olmak için yeniden düzenleme muhtemelen maliyet değmez.
Doval


@Doval: Lütfen durum bilgisi olmayan bir telefon (SIP UserAgent) gibi bir şeyin nasıl yapılacağını açıklayın. Bu birimin beklenen davranışı, bir durum geçiş şeması kullanılarak RFC'de belirtilir.
Bart van Ingen Schenau

Testlerinizi kopyalıyor / yapıştırıyor / düzenliyor musunuz veya ortak kurulum / yıkım / işlevselliği paylaşmak için yardımcı yöntemler yazıyor musunuz? Bazı test vakaları kesinlikle uzun ve şişkin olabilirken, bu kadar yaygın olmamalıdır. Durum bilgisi olan bir sistemde, son durumun bir parametre olduğu ortak bir kurulum rutini beklerdim ve bu rutin sizi test etmek istediğiniz duruma getirir. Ayrıca, her testin sonunda sizi bilinen başlangıç ​​durumuna geri döndüren bir yırtma yöntemim olacaktır (gerekirse), bir sonraki test başladığında kurulum yönteminiz düzgün çalışacaktır.
Dunk

Bir teğet üzerinde, ancak Devlet diyagramlarının bir RFC'de olsa bile bir uygulama kararı değil bir iletişim aracı olduğunu da ekleyeceğim. Açıklanan işlevselliği karşıladığınız sürece standardı karşılarsınız. Gerçekten karmaşık genel geçiş uygulamalarını (RFC'lerde tanımlandığı gibi) gerçekten basit genel işleme işlevselliğine dönüştürdüğüm birkaç kez yaşadım. Bir örnek, "gizli" ortak öğeleri yeniden adlandırdığınızda, 5 eyalet hakkında birkaç bayrak dışında aynı şeyi yaptığını fark ettikten sonra birkaç bin satır koddan kurtulduğumu hatırlıyorum.
Dunk

Yanıtlar:


15

Perspektif:

Öyleyse bir adım geriye gidip TDD'nin bize yardım etmeye çalıştığını soralım. TDD, kodumuzun doğru olup olmadığını belirlememize yardımcı olmaya çalışıyor. Doğru olarak, "kod iş gereksinimlerini karşılıyor mu?" Satış noktası, gelecekte değişikliklerin gerekli olacağını biliyoruz ve bu değişiklikleri yaptıktan sonra kodumuzun doğru kalmasını sağlamak istiyoruz.

Bu perspektifi yükseltirim, çünkü detaylarda kaybolmanın ve elde etmeye çalıştığımız şeyleri gözden kaçırmanın kolay olduğunu düşünüyorum.

İlkeler - SAP:

TDD'de uzman olmasam da, Tek Onaylama Prensibi'nin (SAP) öğretmeye çalıştığının bir parçası olduğunu düşünüyorum. SAP "her seferinde bir şeyi test et" şeklinde yeniden ifade edilebilir. Ancak TOTAT, dili SAP kadar kolay yuvarlamaz.

Her seferinde bir şeyi test etmek, bir vakaya odaklanmanız anlamına gelir; bir yol; bir sınır koşulu; bir hata durumu; biri ne olursa olsun test başına. Ve bunun arkasındaki itici fikir, test durumu başarısız olduğunda neyin kırıldığını bilmeniz gerektiğidir, böylece sorunu daha hızlı çözebilirsiniz. Bir test içinde birden fazla koşulu (yani birden fazla şey) test ederseniz ve test başarısız olursa, ellerinizde çok daha fazla işiniz olur. Öncelikle birden fazla vakanın hangisinin başarısız olduğunu tanımlamanız ve ardından bu vakanın neden başarısız olduğunu anlamanız gerekir .

Bir seferde bir şeyi test ederseniz, arama kapsamınız çok daha küçüktür ve kusur daha hızlı tanımlanır. "Her seferinde bir şeyi test et" seçeneğinin, bir kerede birden fazla işlem çıktısına bakmanıza gerek olmadığını unutmayın. Örneğin, bir "bilinen iyi yol" test ederken, belirli bir sonuç değeri fooyanı sıra başka bir değer görmek için bekleyebilirsiniz barve bunu testimin bir foo != barparçası olarak doğrulayabilir . Anahtar, çıkış kontrollerini test edilen duruma göre mantıksal olarak gruplandırmaktır.

İlkeler - PMP:

Benzer şekilde, sanırım Özel Yöntem Prensibi'nin (PMP) bize ne öğretmesi gerektiği konusunda biraz eksiksiniz. PMP, sisteme bir kara kutu gibi davranmamızı teşvik ediyor. Belirli bir girdi için, belirli bir çıktı almalısınız. Kara kutunun çıktıyı nasıl oluşturduğu umrunda değil. Yalnızca çıktılarınızın girdilerinizle hizalanmasını önemsersiniz.

PMP, kodunuzun API yönlerine bakmak için gerçekten iyi bir perspektiftir. Ayrıca, test etmek zorunda olduğunuz şeyleri kapsamanıza da yardımcı olabilir. Arayüz noktalarınızı tanımlayın ve sözleşmelerinin şartlarını karşıladıklarını doğrulayın. Arayüzün arkasındaki (diğer adıyla özel) yöntemlerin işlerini nasıl yaptığına dikkat etmeniz gerekmez. Sadece yapmaları gerekeni yaptığını doğrulamanız gerekiyor.


Uygulamalı TDD ( sizin için )

Durumunuz sıradan bir uygulamanın ötesinde biraz kırışıklık gösterir. Uygulamanızın yöntemleri durumsaldır, bu nedenle çıktıları yalnızca girişe değil, daha önce yapılanlara da bağlıdır. Eminim <insert some lecture>burada devletin korkunç ve falan filan falan olması gerekir, ama bu gerçekten probleminizi çözmeye yardımcı olmaz.

Çeşitli potansiyel durumları ve bir geçişi tetiklemek için ne yapılması gerektiğini gösteren bir çeşit durum diyagramı tablonuz olduğunu varsayacağım. Bunu yapmazsanız, bu sistem için iş gereksinimlerini ifade etmeye yardımcı olacağından buna ihtiyacınız olacak.

Testler: İlk olarak, durum değişikliğini yürürlüğe koyan bir dizi testle sonuçlanacaksınız. İdeal olarak, meydana gelebilecek tüm durum değişikliklerini uygulayan testlere sahip olacaksınız, ancak tam olarak gitmeniz gerekmeyebilecek birkaç senaryo görebiliyorum.

Ardından, veri işlemeyi doğrulamak için testler oluşturmanız gerekir. Veri işleme testlerini oluşturduğunuzda bu durum testlerinden bazıları yeniden kullanılacaktır. Örneğin, ve durumlarına Foo()dayalı olarak farklı çıktılara sahip bir yönteminiz olduğunu varsayalım . Eğer kullanmak isteyeceksiniz "ne zaman çıktı test etmek için bir kurulum adımı olarak testi içindedir ".InitState1ChangeFooToState1Foo()State1

Bu yaklaşımın arkasında bahsetmek istediğim bazı çıkarımlar var. Spoiler, işte safları çileden çıkaracağım

Öncelikle, bir şeyi bir durumda test ve başka bir durumda kurulum olarak kullandığınızı kabul etmelisiniz. Bir yandan, bu doğrudan SAP'nin ihlali gibi görünüyor. Ancak mantıklı ChangeFooToState1olarak iki amaca sahip olursanız, yine de SAP'nin bize ne öğrettiğinin ruhunu karşılıyorsunuz demektir. Foo()Durumların değiştiğinden emin olmanız gerektiğinde , ChangeFooToState1test olarak kullanırsınız . Ve ne zaman " Foo()in State1" çıkışını doğrulamanız gerektiğinde ChangeFooToState1kurulum olarak kullanıyorsunuz .

İkinci madde, pratik açıdan, sisteminiz için tamamen rastgele birim testi istemeyeceğinizdir. Çıkış doğrulama testlerini çalıştırmadan önce tüm durum değişikliği testlerini çalıştırmalısınız. SAP, bu siparişin arkasındaki ana prensiptir. Neyin belirgin olması gerektiğini belirtmek için - test olarak başarısız olursa bir şeyi kurulum olarak kullanamazsınız.

Bir araya getirmek:

Durum diyagramınızı kullanarak geçişleri kapsayan testler oluşturacaksınız. Yine, diyagramınızı kullanarak, durum tarafından yönlendirilen tüm giriş / çıkış veri işleme durumlarını kapsayacak şekilde testler oluşturursunuz.

Bu yaklaşımı izlerseniz, bloated, complicated, long, and difficult to write testlerin yönetilmesi biraz daha kolay olmalıdır. Genel olarak, daha küçük olmalılar ve daha özlü olmalılar (yani daha az karmaşık). Testlerin daha ayrıştırılmış veya modüler olduğunu da fark etmelisiniz.

Şimdi, sürecin tamamen ağrısız olacağını söylemiyorum çünkü iyi testler yazmak biraz çaba gerektiriyor. Ve bazıları hala zor olacak çünkü birkaç vakanızın üzerinde ikinci bir parametre (durum) eşliyorsunuz. Ve bir kenara, vatansız bir sistemin neden testler yapmak için daha kolay olduğu biraz daha açık olmalıdır. Ancak bu yaklaşımı uygulamanıza uyarlarsanız, uygulamanızın doğru çalıştığını kanıtlayabildiğinizi bulmalısınız.


11

Kurulum ayrıntılarını genellikle işlevlere ayırırsınız, böylece kendinizi tekrarlamanız gerekmez. Bu şekilde işlevsellik değişirse testte yalnızca bir yerde değiştirmeniz gerekir.

Bununla birlikte, normalde kurulum işlevlerinizi bile şişkin, karmaşık veya uzun olarak tanımlamak istemezsiniz. Bu, arayüzünüzün yeniden düzenlenmesi gerektiğini gösteren bir işarettir, çünkü testlerinizin kullanımı zorsa, gerçek kodunuzun da kullanımı zordur.

Bu genellikle bir sınıfa çok fazla şey koymanın bir işaretidir. Durum bilgisi olan gereksinimleriniz varsa, durumu yöneten bir sınıfa ihtiyacınız vardır, başka bir şey yoktur. Onu destekleyen sınıflar vatansız olmalıdır. SIP örneğiniz için, bir paketi ayrıştırma işlemi tamamen vatansız olmalıdır. Bir paketi ayrıştıran, daha sonra sipStateController.receiveInvite()devlet geçişlerini yönetmek gibi bir şey çağıran bir sınıfınız olabilir , bu da kendisi telefon çalmak gibi şeyler yapmak için diğer durumsuz sınıfları çağırır.

Bu, durum makine sınıfı için birim testinin ayarlanmasını birkaç yöntem çağrısı için basit bir konu haline getirir. Durum makine birimi testleri kurulumunuzda paketlerin hazırlanması gerekiyorsa, bu sınıfa çok fazla şey koydunuz. Benzer şekilde, paket ayrıştırıcı sınıfınızın durum makine sınıfı için bir sahte kullanarak kurulum kodu oluşturmak için nispeten basit olması gerekir.

Başka bir deyişle, durumdan tamamen kaçınamazsınız, ancak onu en aza indirebilir ve izole edebilirsiniz.


Sadece kayıt için, SIP örneği OP'den değil, benimdi. Bazı eyalet makinelerinin, belirli bir test için doğru duruma getirmeleri için birkaç yöntem çağrısından fazlasına ihtiyacı olabilir.
Bart van Ingen Schenau

+1 "durumu tamamen ortadan kaldıramazsınız, ancak simge durumuna küçültebilir ve yalıtabilirsiniz." Kabul edemedim. Devlet yazılımda gerekli bir kötülüktür.
Brandon

0

TDD'nin ana fikri, önce testler yazarak, en azından test edilmesi kolay bir sisteme ulaşmanızdır. Umarım işe yarar, bakım yapılabilir, iyi belgelendirilmiş ve benzeridir, ancak değilse, en azından test etmek hala kolaydır.

Yani, TDD yaparsanız ve test edilmesi zor bir sistemle sonuçlanırsanız, bir şeyler ters gitti. Belki de özel olan bazı şeyler herkese açık olmalıdır, çünkü test için onlara ihtiyacınız vardır. Belki de doğru soyutlama düzeyinde çalışmıyorsunuzdur; bir liste kadar basit bir şey bir düzeyde durum bilgisi, diğerinde bir değerdir. Ya da belki bağlamınızda geçerli olmayan tavsiyelere çok fazla ağırlık veriyorsunuz ya da probleminiz çok zor. Ya da, elbette, tasarımınız sadece kötü.

Nedeni ne olursa olsun, basit test koduyla daha test edilebilir hale getirmek için geri dönüp sisteminizi tekrar yazmayacaksınız. Bu yüzden muhtemelen en iyi plan, biraz daha süslü test tekniklerini kullanmaktır, örneğin:

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.