Bazı basit işlevleri kodlamak için TDD'yi kullanmaya nasıl başlayabilirim?


9

Temelde TDD'nin özü var. Ben faydalı olduğunu sattım ve MSTEST çerçevesine makul bir komut var. Ancak, bugüne kadar bunu birincil gelişim yöntemi olarak kullanmak için mezun olamadım. Çoğunlukla, test uygulamaları (geleneksel yaklaşımım) olarak konsol uygulamaları yazmak için bir vekil olarak kullanıyorum.

Benim için en yararlı şey, regresyon testinin rolünü emme biçimidir.

Henüz bildiğim resmin bir başka büyük kısmı olan çeşitli test edilebilir davranışları izole eden hiçbir şey inşa etmedim.

Bu soru, aşağıdaki geliştirme görevi için ilk test (ler) için ne yazabileceğime dair işaretçiler sormaktır: Yapımcı / tüketici tarzında görev yürütmeyi içine alan kod üretmek istiyorum.

Bu kodu yazdıktan sonra durdum ve bu soruyu yazmaya karar verdim (bu sefer TDD'yi gerçekten kullanabilir miyim diye merak ediyorum)

Kod:

interface ITask
{
    Guid TaskId { get; }
    bool IsComplete { get; }
    bool IsFailed { get; }
    bool IsRunning { get; }
}

interface ITaskContainer
{
    Guid AddTask(ICommand action);
}

interface ICommand
{
    string CommandName { get; }
    Dictionary<string, object> Parameters { get; }
    void Execute();
}

Testleri önce ve sonra arayüzleri yazmış olmalısınız! Bütün fikir TDD'nin API'niz için olmasıdır.

1
Henüz mevcut olmayan arayüzler için test nasıl yazılır? Derlemeyecekler bile.
Robert Harvey

5
Bu ilk başarısız testin.
cori

Yanıtlar:


10

Bu konsept ile başlayarak:
1) İstediğiniz davranışla başlayın. Bunun için bir test yazın. Bkz. Test başarısız.
2) Testi geçmek için yeterli kodu yazın. Tüm testlerin geçtiğini görün.
3) Gereksiz / özensiz kod -> refactor arayın. Bkz. Testler hala geçiyor. Git 1

Diyelim ki # 1'de yeni bir komut oluşturmak istediğinizi varsayalım (komutun nasıl çalışacağına uzanıyorum, bu yüzden bana katlanın). (Ayrıca, aşırı TDD yerine biraz pragmatik olacağım)

Yeni komut MakeMyLunch olarak adlandırılır, bu yüzden önce onu başlatmak ve komut adını almak için bir test oluşturursunuz:

@Test
public void instantiateMakeMyLunch() {
   ICommand command = new MakeMyLunchCommand();
   assertEquals("makeMyLunch",command.getCommandName());
}

Bu başarısız olur, sizi yeni komut sınıfını oluşturmaya ve adını geri döndürmeye zorlar (purist, bunun TDD'nin iki tur olduğunu söyler, 1 değil). Böylece sınıfı oluşturursunuz ve komut adını döndürmek de dahil olmak üzere ICommand arabirimini uygulamasını istersiniz. Tüm testleri çalıştırmak artık tüm geçişleri gösteriyor, bu yüzden yeniden düzenleme fırsatlarını aramaya devam ediyorsunuz. Muhtemelen yok.

Sonra yürütmek uygulamak istiyorum. Bu yüzden şunu sormalısınız: "MakeMyLunch" ın başarılı bir şekilde öğle yemeğimi yaptığını nasıl bilebilirim? Bu işlem nedeniyle sistemde ne değişiyor? Bunu test edebilir miyim?

Şunu test etmenin kolay olduğunu varsayalım:

@Test
public void checkThatMakeMyLunchIsSuccessful() {
   ICommand command = new MakeMyLunchCommand();
   command.execute();
   assertTrue( Lunch.isReady() );
}

Diğer zamanlarda, bu daha zordur ve gerçekten yapmak istediğiniz şey, test edilen deneğin (MakeMyLunchCommand) sorumluluklarını test etmektir. Belki de MakeMyLunchCommand'ın sorumluluğu Buzdolabı ve Mikrodalga ile etkileşim kurmaktır. Bu yüzden test etmek için bir sahte Buzdolabı ve sahte Mikrodalga kullanabilirsiniz. [iki örnek sahte çerçeve Mockito ve nMock'tur veya buraya bakın .]

Bu durumda aşağıdaki sahte kod gibi bir şey yaparsınız:

@Test
public void checkThatMakeMyLunchIsSuccessful() {
   Fridge mockFridge = mock(Fridge);
   Microwave mockMicrowave = mock(Microwave);
   ICommand command = new MakeMyLunchCommand( mockFridge, mockMicrowave );
   command.execute();
   mockFramework.assertCalled( mockFridge.removeFood );
   mockFramework.assertCalled( microwave.turnon );
}

Purist, sınıfınızın sorumluluğunu test ediyor - diğer sınıflarla etkileşimleri (komut buzdolabını açtı ve mikrodalga fırını açtı mı?).

Pragmatist bir grup sınıf için test ve sonuç için test diyor (öğle yemeğiniz hazır mı?).

Sisteminiz için uygun olan dengeyi bulun.

(Not: belki de arayüz yapınıza çok erken vardığınızı düşünün. Belki de birim testlerinizi ve uygulamalarınızı yazarken bunun gelişmesine izin verebilirsiniz ve 3. adımda ortak arayüz fırsatını "fark edersiniz").


arayüzümü önceden yazmamış olsaydım, hangi soru Execute () yönteminin yaratılmasına yol açacaktı - TDD'ye ilk girişimlerimden bazıları ek işlevselliği teşvik etmek için bir "adım" olmadığında durdu - alıyorum duygu, kaldırılması gereken gizli bir tavuk / yumurta sorunu var
Aaron Anodide

1
İyi soru! Yaptığınız tek komut "MakeMyLunchCommand" ise, yöntem ".makeMyLunch ()" ile başlamış olabilir. Hangi iyi olurdu. Sonra başka bir komut ("NapCommand.takeNap ()") yaparsınız. Farklı yöntemlerle ilgili hala bir sorun yok. Daha sonra, onu ICommand arayüzüne genellemek zorunda kaldığınız ekosisteminizde kullanmaya başlarsınız. Genel olarak, genellemeyi genellikle son sorumlu ana kadar geciktirirsiniz, çünkü YAGNI [ en.wikipedia.org/wiki/You_ain't_gonna_need_it ] =) Diğer zamanlarda, oraya başlayacağınızı biliyorsunuz, böylece başlayacaksınız.
jayraynet

(Ayrıca burada varsayım, yöntem adları önemsiz şeyleri yeniden düzenleme yapan modern bir IDE kullandığınızdır)
jayraynet

1
tavsiye için tekrar teşekkürler, nihayet tüm parçaları ve bunların nasıl oturduğunu görmek benim için bir kilometre taşı - ve evet, hızlı refactor alet
kutumda
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.