在很多情况下,我可能有一个具有某些行为的现有类:
class Lion
{
public void Eat(Herbivore herbivore) { ... }
}
我有一个单元测试
[TestMethod]
public void Lion_can_eat_herbivore()
{
var herbivore = buildHerbivoreForEating();
var test = BuildLionForTest();
test.Eat(herbivore);
Assert.IsEaten(herbivore);
}
现在,发生的事情是我需要创建一个与Lion具有类似行为的Tiger类:
class Tiger
{
public void Eat(Herbivore herbivore) { ... }
}
...由于我想要相同的行为,因此我需要运行相同的测试,因此我需要执行以下操作:
interface IHerbivoreEater
{
void Eat(Herbivore herbivore);
}
...然后重构测试:
[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);
}
...然后为我的新Tiger
课程添加另一个测试:
[TestMethod]
public void Tiger_can_eat_herbivore()
{
IHerbivoreEater_can_eat_herbivore(BuildTigerForTest);
}
...然后我重构我Lion
和的Tiger
类(通常通过继承,但有时通过组合):
class Lion : HerbivoreEater { }
class Tiger : HerbivoreEater { }
abstract class HerbivoreEater : IHerbivoreEater
{
public void Eat(Herbivore herbivore) { ... }
}
...一切都很好。但是,由于该功能已包含在HerbivoreEater
类中,因此现在感觉在每个子类上对这些行为中的每一个进行测试都存在问题。然而,实际上是这些子类被消耗了,并且仅仅是实现细节,它们碰巧共享重叠的行为(例如Lions
,Tigers
可能具有完全不同的最终用途)。
多次测试相同的代码似乎是多余的,但是在某些情况下,子类可以并且确实会覆盖基类的功能(是的,它可能违反了LSP,但请直面它,IHerbivoreEater
它只是一个方便的测试接口-它对最终用户可能没有关系)。因此,我认为这些测试确实有价值。
在这种情况下其他人会做什么?您只是将测试移至基类,还是测试所有子类的预期行为?
编辑:
基于@pdr的答案,我认为我们应该考虑这一点:IHerbivoreEater
仅仅是方法签名合同;它没有指定行为。例如:
[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
行为放在基类中,则所有子类都应表现出相同的Eat
行为。但是,我说的是2个相对不相关的类,它们碰巧共享一种行为。例如,考虑的Fly
行为,Brick
并且Person
我们可以假设它表现出类似的飞行行为,但是让它们从通用基类派生并不一定有意义。
Animal
包含的类Eat
吗?所有动物都进食,因此Tiger
andLion
类可以从动物继承。