Verilerinizi tüm birim testlerinde kodlamanız gerekiyor mu?


33

Çoğu birim test dersleri / örnekleri, genellikle her bir test için test edilecek verilerin tanımlanmasını içerir. Sanırım bu, "her şey yalıtımlı olarak test edilmeli" teorisinin bir parçası.

Ancak , çok sayıda DI içeren çok katmanlı uygulamalarla çalışırken , her testi ayarlamak için gereken kodun çok uzun sürdüğünü buldum. Bunun yerine, devralınacak çok sayıda test iskelesi olan, şimdiden devralabildiğim birkaç testbase sınıfı oluşturdum.

Bunun bir parçası olarak, her bir "tabloda" genellikle yalnızca bir veya iki satır olmasına rağmen, çalışan bir uygulamanın veritabanını temsil eden sahte veri kümeleri yapıyorum.

Tüm ünite testlerinde test verilerinin çoğunluğunun hepsini değilse de önceden tanımlaması kabul edilmiş bir uygulama mıdır?

Güncelleştirme

Aşağıdaki yorumlardan, birim testinden daha fazla entegrasyon yaptığımı hissediyorum.

Şu anki projem, ASP.NET MVC, Önce Varlık Çerçeve Kodu Üzerinden Çalışma Birimi ve test için Adedi kullanan. UW ve havuzlarla alay ettim ama gerçek iş mantığı sınıflarını kullanıyorum ve kontrol cihazının eylemlerini test ediyorum. Testler genellikle UW'nin yerine getirildiğini kontrol eder, örneğin:

[TestClass]
public class SetupControllerTests : SetupControllerTestBase {
  [TestMethod]
  public void UserInvite_ExistingUser_DoesntInsertNewUser() {
    // Arrange
    var model = new Mandy.App.Models.Setup.UserInvite() {
      Email = userData.First().Email
    };

    // Act
    setupController.UserInvite(model);

    // Assert
    mockUserSet.Verify(m => m.Add(It.IsAny<UserProfile>()), Times.Never);
    mockUnitOfWork.Verify(m => m.Commit(), Times.Once);
  }
}

SetupControllerTestBaseUW alayını kuruyor ve onu başlatıyor userLogic.

Testlerin birçoğu veritabanında mevcut bir kullanıcı veya ürünün olmasını gerektirir, bu nedenle UoW sahte cihazının ne getirdiğini önceden doldurdum, bu örnekte userData, sadece IList<User>tek bir kullanıcı kaydına sahip.


4
Öğreticiler / örneklerle ilgili sorun, basit olmaları gerektiğidir, ancak basit bir örnek üzerinde karmaşık bir soruna çözüm gösteremezsiniz. Aletin makul büyüklükteki gerçek projelerde nasıl kullanıldığını açıklayan "vaka çalışmaları" eşlik etmelidir, ancak nadirendirler.
Jan Hudec

Belki de tamamen memnun olmadığınız bazı küçük kod örnekleri ekleyebilirsiniz.
Luc Franken

Bir testi çalıştırmak için çok fazla kurulum koduna ihtiyacınız olursa , fonksiyonel bir test yapma riski vardır. Kod değiştirdiğinizde test başarısız olursa ancak kodda yanlış bir şey yoktur. Kesinlikle işlevsel bir test.
Tepki

"XUnit Test Patterns" kitabı, tekrar kullanılabilir demirbaşlar ve yardımcılar için güçlü bir örnek teşkil eder. Test kodu, diğer tüm kodlar kadar korunmalıdır.
Chuck Krutsinger

Yanıtlar:


25

Sonuçta, olabildiğince sonuç almak için mümkün olduğunca az kod yazmak istiyorsunuz. Birden fazla testte aynı kodun çoğuna sahip olmak a) kopyala-yapıştır kodlamasıyla sonuçlanma eğilimindedir ve b), bir yöntem imzası değişirse sonuçta çok sayıda kırık testi düzeltmek zorunda kalacağınız anlamına gelir.

Bana, rutin olarak kullandığım birçok veri türünü sağlayan standart TestHelper sınıflarına sahip olma yaklaşımını kullanıyorum, böylece testlerimin her seferinde tam olarak ne alacağımı sorgulaması ve bilmesi için standart varlık kümeleri veya DTO sınıfları oluşturabilirim. Bu yüzden TestHelper.GetFooRange( 0, 100 )tüm bağımlı sınıfları / alanları ayarlanmış olarak 100 Foo nesnesi aralığına geçebilirim .

Özellikle, bir şeylerin doğru çalışması için bulunması gereken ORM tipi bir sistemde yapılandırılmış karmaşık ilişkilerin olduğu, ancak çok fazla zaman kazandırabilecek bu test için mutlaka önemli bir durum olmadığı durumlarda.

Veri seviyesine yakın test ettiğim durumlarda, bazen benzer bir şekilde sorgulanabilen depo sınıfımın bir test sürümünü oluşturuyorum (yine, bu bir ORM tipi ortamdaydı ve gerçek veritabanı), çünkü sorgulara verilen cevapları alay etmek çok iş ve çoğu zaman sadece küçük faydalar sağlar.

Ünite testlerinde olsa dikkat edilmesi gereken bazı şeyler var:

  • Emin olun mocks olan mocks . Birim testi yapıyorsanız , test edilen sınıf çevresinde işlem yapan sınıflar sahte nesneler olmalıdır . DTO / varlık tipi sınıflarınız gerçek olabilir, ancak sınıflar işlem yapıyorsa, onları alay etmek zorundasınız. Aksi takdirde, destekleyici kod değiştiğinde ve testleriniz başarısız olmaya başladığında, hangi değişikliğin ne olduğunu bulmak için daha uzun süre aramanız gerekir. aslında soruna neden oldu.
  • Sınıflarınızı test ettiğinizden emin olun . Bazen bir birim test paketi üzerinden bakıldığında, testlerin yarısının alaycı çerçeveyi gerçekten test etmesi gereken gerçek koddan daha fazla test ettiği açıktır.
  • Sahte / destekleyici nesneleri tekrar kullanmayın Bu bir biggie - kişi kod destekleme ünitesi testleriyle akıllıca çalışmaya başladığında, yanlışlıkla tahmin edilemeyen etkileri olabilecek testler arasında kalıcı nesneler oluşturmak gerçekten kolaydır. Örneğin, dün kendi başına koşarken geçen, sınıftaki tüm testler çalıştırıldığında geçen ancak test grubunun tamamı çalıştırıldığında başarısız olan bir test yaptım. Bir test yardımcısında sinsi bir statik nesne olduğu ortaya çıktı, yarattığımda kesinlikle bir soruna yol açmayacaktı . Sadece hatırlayın: Testin başında, her şey yaratılır, testin sonunda her şey mahvolur.

10

Testinizin amacını daha okunaklı kılan şey ne ise.

Genel bir kural olarak:

Veriler testin bir parçasıysa (örn. 7 durumlu satırlar yazdırmamalı), testte kodlayın, böylece yazarın ne olması gerektiği açıktır.

Veriler, üzerinde çalışacak bir şey olduğundan emin olmak için yalnızca doldurucuysa (örn. İşlem hizmeti istisna atarsa ​​kaydı eksiksiz olarak işaretlememelidir), o zaman elbette bir BuildDummyData yöntemine veya alakasız verileri testten uzak tutan bir test sınıfına sahip olun .

Ancak, ikincisinin iyi bir örneği düşünmek için mücadele ettiğime dikkat edin. Birim test fikstüründe bunlardan birçoğunuz varsa, muhtemelen çözmeniz gereken farklı bir probleminiz var ... belki de test edilen yöntem çok karmaşıktır.


+1 katılıyorum. Bu, test ettiği şeyin birim testi için sıkı sıkıya bağlı olduğu gibi kokuyor.
Reactgular 27:13

5

Farklı test yöntemleri

İlk önce ne yaptığınızı tanımlayın: Birim testi veya entegrasyon testi . Büyük olasılıkla yalnızca bir sınıfı test ettiğiniz için katman sayısı ünite testi için önemli değildir. Gerisini alay ettin. Entegrasyon testi için çoklu katmanları test etmeniz kaçınılmazdır. Yerinde iyi bir birim testiniz varsa, hile entegrasyon testlerini çok karmaşık yapmamaktır.

Ünite testleriniz iyi ise, entegrasyon testi yaparken tüm detayları test etmek zorunda kalmazsınız.

Kullandığımız terimler, bunlar biraz platforma bağımlı, ancak bunları neredeyse tüm test / geliştirme platformlarında bulabilirsiniz:

Örnek uygulama

Kullandığınız teknolojiye bağlı olarak isimleri farklı olabilir, ancak bunu örnek olarak kullanacağım:

Model Product, ProductsController ve ürünlerle birlikte bir HTML tablosu oluşturan bir dizin görünümüne sahip basit bir CRUD uygulamanız varsa:

Uygulamanın sonucu, aktif olan tüm ürünlerin bir listesini içeren bir HTML tablosu gösteriliyor.

Birim testi

model

Model kolayca test edebilirsiniz. Bunun için farklı yöntemler var; demirbaş kullanıyoruz. Bence buna "sahte veri setleri" diyorsun. Böylece her test çalıştırılmadan önce, tabloyu yaratır ve orijinal verileri koyarız. Çoğu platformun bunun için yöntemleri var. Örneğin, test sınıfınızda, her testten önce çalıştırılan bir setUp () yöntemi.

Daha sonra testimizi yapıyoruz, örneğin: testGetAllActive products.

Bu yüzden doğrudan bir test veritabanına test ediyoruz. Veri kaynağını alamıyoruz; Her zaman aynı yaparız. Bu, örneğin veritabanının yeni bir versiyonuyla test etmemize olanak tanır ve herhangi bir sorgu sorunu ortaya çıkacaktır.

Gerçek dünyada her zaman% 100 tek sorumluluk izleyemezsin . Bunu daha iyi yapmak istiyorsanız, alay ettiğiniz bir veri kaynağını kullanabilirsiniz. Zaten var olan teknolojiyi test etmek gibi hissettiren bizim için (ORM kullanıyoruz). Ayrıca testler çok daha karmaşık hale gelir ve sorguları gerçekten test etmezler. Bu yüzden böyle tutarız.

Sabit kodlanmış veriler, fikstürlerde ayrı olarak saklanır. Yani fikstür bir create table deyimi ve kullandığımız kayıtlar için ekler içeren bir SQL dosyası gibidir. Çok fazla kayıtla test etme gereği duymadıkça onları küçük tutuyoruz.

class ProductModel {
  public function getAllActive() {
    return $this->find('all', array('conditions' => array('active' => 1)));
  }
}

kontrolör

Denetleyicinin daha fazla çalışmaya ihtiyacı var çünkü modeli test etmek istemiyoruz. Öyleyse yaptığımız şey, modelle dalga geçmek. Bunun anlamı: Testler: kayıt listesini döndürmesi gereken index () yöntemi.

Bu yüzden model metodu getAllActive () ile alay ediyoruz ve içine sabit veri ekliyoruz (örneğin iki kayıt). Şimdi denetleyicinin görünüme gönderdiği verileri test ediyoruz ve bu iki kaydı gerçekten geri alıp almadığımızı karşılaştırıyoruz.

function testProductIndexLoggedIn() {
  $this->setLoggedIn();
  $this->ProductsController->mock('ProductModel', 'index', function(return array(your records) ));
  $result=$this->ProductsController->index();
  $this->assertEquals(2, count($result['products']));
}

Bu yeterli. Denetleyiciye çok az işlevsellik eklemeye çalışıyoruz çünkü bu testi zorlaştırıyor. Ama elbette içinde her zaman bir kod var. Örneğin, aşağıdakiler gibi gereksinimleri test ediyoruz: Bu iki kaydı yalnızca oturum açtıysanız gösterin.

Bu nedenle, kontrol cihazının normal olarak bir sahte ve küçük bir parça kodlanmış verilere ihtiyacı vardır. Bir giriş sistemi için belki başka bir tane. Testimizde bunun için yardımcı bir yöntemimiz var: setLoggedIn (). Bu, giriş yaparak veya giriş yapmadan test etmeyi kolaylaştırır.

class ProductsController {
  public function index() {
    if($this->loggedIn()) {
      $this->set('products', $this->ProductModel->getAllActive());
    }
  }
}

Görünümler

Görüntüleme testi zor. İlk önce tekrarlayan mantığı ayırıyoruz. Yardımcılara koyduk ve sıkı bir şekilde bu sınıfları test ettik. Hep aynı çıktıyı bekliyoruz. Örneğin, geneHtmlTableFromArray ().

O zaman projeye özel görüşlerimiz var. Bunları test etmiyoruz. Bunları test etmek gerçekten istenmiyor. Onları entegrasyon testleri için saklıyoruz. Çünkü kodların çoğunu görüşlere dönüştürdüğümüz için burada daha düşük bir risk var.

Bunları test etmeye başlarsanız, muhtemelen çoğu projeniz için kullanışlı olmayan bir HTML parçasını değiştirdiğinizde testlerinizi değiştirmeniz gerekir.

echo $this->tableHelper->generateHtmlTableFromArray($products);

Entegrasyon testi

Platformunuza bağlı olarak, burada kullanıcı hikayeleri vb. İle çalışabilirsiniz. Selenyum veya diğer benzer çözümler gibi web tabanlı olabilir .

Genel olarak veri tabanını sadece demirbaşlar ile yüklüyoruz ve hangi verinin mevcut olması gerektiğini iddia ediyoruz. Tam entegrasyon testi için genellikle çok genel şartlar kullanıyoruz. Yani: Ürünü aktif olarak ayarlayın ve ardından ürünün kullanılabilir olup olmadığını kontrol edin.

Doğru alanların mevcut olup olmadığı gibi her şeyi bir daha test etmiyoruz. Burada daha büyük gereksinimleri test ediyoruz. Çünkü testlerimizi denetleyiciden veya görünümden çoğaltmak istemiyoruz. Bir şey uygulamanızın gerçekten önemli bir parçasıysa veya güvenlik nedenleriyle (parola mevcut DEĞİLDİR) ise, doğru olduğundan emin olmak için bunları ekleriz.

Sabit kodlanmış veriler armatürlerde saklanır.

function testIntegrationProductIndexLoggedIn() {
  $this->setLoggedIn();
  $result=$this->request('products/index');

  $expected='<table';
  $this->assertContains($expected, $result);

  // Some content from the fixture record
  $expected='<td>Product 1 name</td>';
  $this->assertContains($expected, $result);
}

Bu tamamen farklı bir soru için harika bir cevap.
pdr

Geri dönüşünüz için teşekkür ederiz. Çok açık bir şekilde bahsetmediğim konusunda haklı olabilirsin. Ayrıntılı cevabın sebebi, sorulan soruyu test ederken en zor şeylerden birini gördüğümdür. İzolasyonda test etmenin farklı test türlerine nasıl uyduğuna genel bakış. Bu yüzden her bölüme verilerin nasıl işlendiğini (veya ayrıldığını) ekledim. Daha açık bir şekilde anladım mı bir bakacağım.
Luc Franken

Cevap, başka tür sınıfları çağırmadan nasıl test edileceğini açıklamak için bazı kod örnekleriyle güncellendi.
Luc Franken

4

Çok fazla DI ve kablolama içeren testler yazıyorsanız, "gerçek" veri kaynaklarını kullanmaya kadar, muhtemelen düz ünite testi alanından çıktınız ve entegrasyon testi alanına girdiniz.

Entegrasyon testleri için ortak veri kurulum mantığına sahip olmanın kötü bir fikir olmadığını düşünüyorum. Bu tür testlerin temel amacı, her şeyin doğru şekilde yapılandırıldığını kanıtlamaktır. Bu, sisteminizden gönderilen somut verilerden oldukça bağımsızdır.

Birim testler için, bir test sınıfının hedefini tek bir "gerçek" sınıf tutmanızı ve diğer her şeyle alay etmenizi öneririm. O zaman mümkün olduğunca çok sayıda özel / önceki hata yolunu kapladığınızdan emin olmak için test verilerini gerçekten kodlamalısınız.

Testlere yarı sabit kodlu / rastgele bir eleman eklemek için rastgele model fabrikaları tanıtmayı seviyorum. Modelimin bir örneğini kullanan bir testte, bu fabrikaları geçerli, ancak tamamen rastgele bir model nesnesi oluşturmak için kullanıyorum ve ardından yalnızca eldeki test için ilgi çekici olan özellikleri kodladım. Bu yolla, tüm ilgili verileri doğrudan testinizde belirtirken, diğer alakasız verilerin olmadığını ve (belirli bir dereceye kadar) diğer model alanlarında istenmeyen bağımlılıklar olmadığını test etme ihtiyacını da korursunuz.


-1

Bence testleriniz için verilerin çoğunu zor kodlamak oldukça yaygındır.

Belirli bir veri kümesinin bir hatanın oluşmasına neden olduğu basit bir durum düşünün. Düzeltmeyi uygulamak ve hatanın geri gelmemesini sağlamak için özellikle bu veriler için bir birim testi oluşturabilirsiniz. Zaman içinde testlerinizde bir dizi test senaryosunu kapsayan bir dizi veri bulunacaktır.

Önceden tanımlanmış test verileri ayrıca geniş ve bilinen durumları kapsayan bir veri kümesi oluşturmanıza olanak sağlar.

Bununla birlikte, testlerinizde rastgele verilerin bulunmasının da bir değeri olduğunu düşünüyorum.


Sadece ünvanı değil soruyu okudun mu?
Jakob

Testlerinizde rastgele veriler bulundurmanın değeri - Evet, çünkü her hafta bir testte ne olduğunu anlamaya çalışmak gibisi yoktur.
pdr

Hazing / fuzzing / giriş testleri için testlerinizde rastgele veriler bulunmasının değeri vardır. Ama birim testlerinde değil, bu bir kabus olurdu.
glenatron
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.