Birçok durumda, bazı davranışları olan mevcut bir sınıf olabilir:
class Lion
{
public void Eat(Herbivore herbivore) { ... }
}
... ve bir birim testim var ...
[TestMethod]
public void Lion_can_eat_herbivore()
{
var herbivore = buildHerbivoreForEating();
var test = BuildLionForTest();
test.Eat(herbivore);
Assert.IsEaten(herbivore);
}
Şimdi, aslanın kimliğine göre davranışı olan bir Tiger sınıfı yaratmam gerekiyor:
class Tiger
{
public void Eat(Herbivore herbivore) { ... }
}
... ve aynı davranışı istediğim için, aynı testi çalıştırmam gerekiyor, böyle bir şey yapıyorum:
interface IHerbivoreEater
{
void Eat(Herbivore herbivore);
}
... ve testimi yeniden düzenledim:
[TestMethod]
public void Lion_can_eat_herbivore()
{
IHerbivoreEater_can_eat_herbivore(BuildLionForTest);
}
public void IHerbivoreEater_can_eat_herbivore(Func<IHerbivoreEater> builder)
{
var herbivore = buildHerbivoreForEating();
var test = builder();
test.Eat(herbivore);
Assert.IsEaten(herbivore);
}
... ve sonra yeni Tiger
sınıfım için bir test daha ekliyorum :
[TestMethod]
public void Tiger_can_eat_herbivore()
{
IHerbivoreEater_can_eat_herbivore(BuildTigerForTest);
}
... sonra benim planı ayrı Lion
ve Tiger
(ama bazen kompozisyon tarafından, genellikle miras yoluyla) sınıfları:
class Lion : HerbivoreEater { }
class Tiger : HerbivoreEater { }
abstract class HerbivoreEater : IHerbivoreEater
{
public void Eat(Herbivore herbivore) { ... }
}
... ve her şey yolunda. Bununla birlikte, işlevsellik şimdi HerbivoreEater
sınıfta olduğundan, şimdi her alt sınıfta bu davranışların her biri için testlere sahip olmakla ilgili bir sorun var gibi görünüyor. Yine de gerçekte tüketilen alt sınıflar ve üst üste binen davranışları paylaştıkları ( Lions
ve Tigers
tamamen farklı son kullanımları olabileceği) sadece bir uygulama detayı .
Aynı kodu birden çok kez test etmek gereksiz görünüyor, ancak alt sınıfın temel sınıfın işlevselliğini geçersiz kılabildiği ve geçersiz kıldığı durumlar vardır (evet, LSP'yi ihlal edebilir, ancak yüzleşmesine izin verir IHerbivoreEater
, sadece uygun bir test arayüzüdür - son kullanıcı için önemli olmayabilir). Bu testlerin bir değeri var sanırım.
Diğer insanlar bu durumda ne yapar? Testinizi sadece temel sınıfa mı taşıyorsunuz, yoksa tüm alt sınıfları beklenen davranış için mi test ediyorsunuz?
DÜZENLE :
@Pdr tarafından verilen cevaba dayanarak şunu düşünmeliyiz: bu IHerbivoreEater
sadece bir yöntem imza sözleşmesidir; davranış belirtmez. Örneğin:
[TestMethod]
public void Tiger_eats_herbivore_haunches_first()
{
IHerbivoreEater_eats_herbivore_haunches_first(BuildTigerForTest);
}
[TestMethod]
public void Cheetah_eats_herbivore_haunches_first()
{
IHerbivoreEater_eats_herbivore_haunches_first(BuildCheetahForTest);
}
[TestMethod]
public void Lion_eats_herbivore_head_first()
{
IHerbivoreEater_eats_herbivore_head_first(BuildLionForTest);
}
Eat
davranışı temel sınıfa koyarsanız, tüm alt sınıflar aynı Eat
davranışı sergilemelidir . Ancak, bir davranışı paylaşmak için gerçekleşen nispeten ilgisiz 2 sınıftan bahsediyorum. Örneğin, benzer bir uçuş davranışı sergilediğini ve hangi Fly
davranışta bulunduğunu varsayabiliriz, ancak bunların ortak bir temel sınıftan türetilmesi mantıklı değildir. Brick
Person
Animal
içeren bir sınıfınız olmamalıEat
mı? Tüm hayvanlar yer ve bu nedenleTiger
veLion
sınıfı hayvandan miras alabilir.