Strateji modeline göre yeniden düzenlenmiş bir fonksiyon nasıl test edilir?


10

Benim kodda gibi bir işlevi varsa:

class Employee{

    public string calculateTax(string name, int salary)
    {
        switch (name)
        {
            case "Chris":
                doSomething($salary);
            case "David":
                doSomethingDifferent($salary);
            case "Scott":
               doOtherThing($salary);               
       }
}

Normalde Ploymorphism'i bir fabrika sınıfı ve strateji modeli kullanarak kullanmak için bunu yeniden düzenlerdim:

public string calculateTax(string name)
{
    InameHandler nameHandler = NameHandlerFactory::getHandler(name);
    nameHandler->calculateTax($salary);
}

Şimdi TDD kullanıyor olsaydım, calculateTax()yeniden düzenleme işleminden önce orijinal üzerinde çalışan bazı testlerim olurdu .

örn:

calculateTax_givenChrisSalaryBelowThreshold_Expect111(){}    
calculateTax_givenChrisSalaryAboveThreshold_Expect111(){}

calculateTax_givenDavidSalaryBelowThreshold_Expect222(){}   
calculateTax_givenDavidSalaryAboveThreshold_Expect222(){} 

calculateTax_givenScottSalaryBelowThreshold_Expect333(){}
calculateTax_givenScottSalaryAboveThreshold_Expect333(){}

Yeniden düzenleme işleminden sonra bir Fabrika sınıfına NameHandlerFactoryve en az 3 uygulamasına sahip olacağım InameHandler.

Testlerimi yeniden düzenlemeye nasıl devam etmeliyim? Ben birim testini silmelisiniz claculateTax()gelen EmployeeTestsve her uygulama için bir test sınıfı oluşturmak InameHandler?

Fabrika sınıfını da test etmeli miyim?

Yanıtlar:


6

Eski testler, calculateTaxhala olması gerektiği gibi çalıştığını doğrulamak için iyidir . Bununla birlikte, bunun için çok sayıda test senaryosuna ihtiyacınız yoktur, sadece 3 (veya hata işlemeyi de test etmek istiyorsanız, beklenmedik değerleri kullanarak name).

Münferit vakaların her biri (şu anda doSomethingve ark. ' Da uygulanmaktadır ), her bir uygulama ile ilgili iç detayları ve özel vakaları test eden kendi test setine sahip olmalıdır. Yeni kurulumda bu testler ilgili Strateji sınıfında doğrudan testlere dönüştürülebilir / dönüştürülmelidir.

Eski ünite testlerini yalnızca kullandıkları kod ve uyguladığı işlevsellik tamamen ortadan kalktığında kaldırmayı tercih ederim. Aksi takdirde, bu testlere kodlanan bilgi hala konuyla ilgilidir, sadece testlerin kendileri yeniden düzenlenmesi gerekir.

Güncelleme

calculateTax(Onlara yüksek seviye testleri diyelim ) testleri ile bireysel hesaplama stratejileri ( düşük seviye testleri ) testleri arasında bazı tekrarlamalar olabilir - bu sizin uygulamanıza bağlıdır.

Testlerinizin orijinal uygulamasının, belirli vergi hesaplamasının sonucunu ileri sürdüğünü ve bunu hesaplamak için belirli hesaplama stratejisinin kullanıldığını açıkça doğruladığını tahmin ediyorum. Bu şemayı tutarsanız, gerçekten de çoğalmanız olacaktır. Bununla birlikte, @Kristof'un işaret ettiği gibi, sadece doğru türde (sahte) stratejinin seçildiğini ve çağrıldığını doğrulamak için üst düzey testleri alay kullanarak da uygulayabilirsiniz calculateTax. Bu durumda, yüksek ve düşük seviyeli testler arasında herhangi bir tekrar olmayacaktır.

Etkilenen testleri yeniden düzenlemek çok maliyetli değilse, ikinci yaklaşımı tercih ederim. Ancak, gerçek hayatta, bazı büyük yeniden düzenleme yaparken, bana yeterince zaman kazandırırsa test kodu çoğaltma az miktarda tolere ediyorum :-)

Fabrika sınıfını da test etmeli miyim?

Yine değişir. Testlerin calculateTaxfabrikayı etkili bir şekilde test ettiğini unutmayın . Fabrika kodu switchyukarıdaki kod gibi önemsiz bir bloksa , bu testler ihtiyacınız olan tek şey olabilir. Ancak fabrika biraz daha zor şeyler yaparsa, bazı testleri özellikle bunun için yapmak isteyebilirsiniz. Her şey, söz konusu kodun gerçekten işe yaradığından emin olmak için ne kadar test yapmanız gerektiğiyle ilgilidir. Kodu okuduktan veya kod kapsamı verilerini analiz ettikten sonra denenmemiş yürütme yolları görürseniz, bunları uygulamak için biraz daha test yapın. Ardından, kodunuzdan tamamen emin olana kadar bu işlemi tekrarlayın.


Kodu gerçek pratik koduma daha yakın hale getirmek için biraz değiştirdim. Şimdi salaryişleve ikinci bir girdi calculateTax()eklendi. Bu şekilde özgün fonksiyonun test kodunu ve strateji sınıfının 3 uygulamasını kopyalayacağımı düşünüyorum.
Songo

@Songo, lütfen güncellememe bakın.
Péter Török

5

TDD veya birim testi konusunda uzman olmadığımı söyleyerek başlayacağım, ancak bunu nasıl test edeceğim (sözde kod kullanacağım):

CalculateTaxDelegatesToNameHandler()
{
    INameHandlerFactory fakeNameHandlerFactory = Fake(INameHandlerFactory);
    INameHandler fakeNameHandler = Fake(INameHandler);

    A.Call.To(fakeNameHandlerFactory.getHandler("John")).Returns(fakeNameHandler);

    Employee employee = new Employee(fakeNameHandlerFactory);
    employee.CalculateTax("John");

    Assert.That.WasCalled(fakeNameHandler.calculateTax());
}

Bu yüzden olduğunu test ediyorum calculateTax()çalışan sınıfının yöntemi doğru şekilde sorar NameHandlerFactorybir için NameHandlerve daha sonra aramaları calculateTax()iade yöntemini NameHandler.


hmmmm yani testi yerine bir davranış testi yapmalıyım (belirli işlevlerin çağrıldığını test etmeliyim) ve temsilci sınıflar üzerinde değer iddiaları mı yapmalıyım?
Songo

Evet, bunu yapardım. Gerçekten de NameHandlerFactory ve NameHandler için ayrı testler yazardım. Bunlara sahip olduğunuzda, işlevlerini Employee.calculateTax()yöntemde tekrar test etmek için bir neden yoktur . Bu şekilde, yeni bir NameHandler eklediğinizde fazladan Çalışan testi eklemenize gerek kalmaz.
Kristof Claes

3

Bir sınıf alıyorsunuz (her şeyi yapan çalışan) ve 3 sınıf grubu yapıyorsunuz: fabrika, çalışan (sadece bir strateji içeren) ve stratejiler.

Bu yüzden 3 grup test yapın:

  1. Fabrikayı tek başına test edin. Girişleri doğru mu yönetiyor? Bilinmeyen bir kişiyi geçtiğinizde ne olur?
  2. Çalışanı tek başına test edin. Keyfi bir strateji belirleyebilir misiniz ve beklediğiniz gibi çalışıyor mu? Herhangi bir strateji veya fabrika seti yoksa ne olur? (kodda bu mümkün ise)
  3. Stratejileri tek başına test edin. Her biri beklediğiniz stratejiyi gerçekleştiriyor mu? Tek sınır girişlerini tutarlı bir şekilde ele alıyorlar mı?

Elbette tüm mesele için otomatik testler yapabilirsiniz, ancak bunlar artık entegrasyon testleri gibidir ve bu şekilde ele alınmalıdır.


2

Herhangi bir kod yazmadan önce, bir Fabrika testi ile başlayacağım. İhtiyacım olan şeylerle alay etmek için kendimi uygulamaları ve kullanımları düşünmeye zorlayacağım.

Daha sonra bir Fabrika uygulayıp her uygulama için bir test yapmaya devam edeceğim ve son olarak da bu testler için uygulamaların kendisi olacaktı.

Sonunda eski testleri kaldıracağım.


2

Benim düşüncem, hiçbir şey yapmamanız, yani yeni bir test eklememeniz gerektiğidir.

Bunun bir fikir olduğunu vurguluyorum ve aslında nesneden beklentileri nasıl algıladığınıza bağlı. Sınıf kullanıcısının vergi hesaplaması için bir strateji sağlamak istediğini düşünüyor musunuz? Eğer umursamıyorsa, testler bunu yansıtmalı ve birim testlerden yansıyan davranış, sınıfın vergiyi hesaplamak için bir strateji nesnesi kullanmaya başlaması umurunda olmamalıdır.

TDD kullanırken bu problemle birkaç kez karşılaştım. Ana neden, bir dış kaynak (bir dosya, bir DB, bir uzak hizmet, vb.) Gibi bir mimari sınır bağımlılığı demek yerine, bir strateji nesnesinin doğal bir bağımlılık olmadığını düşünüyorum. Doğal bir bağımlılık olmadığından, genellikle sınıfımın davranışını bu stratejiye dayandırmıyorum. İçgüdüm, testlerimi sadece sınıfımdan beklentiler değiştiğinde değiştirmem gerektiğidir.

TDD kullanırken Bob Amca'dan büyük bir mesaj var .

Her ayrı sınıfı test etme eğiliminin TDD'yi öldüren şey olduğunu düşünüyorum. TDD'nin tüm güzelliği, tasarım şemalarını teşvik etmek için testleri kullanmanızdır;

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.