假设以下模板方法实现:
public abstract class Abs
{
void DoYourThing()
{
log.Info("Starting");
try
{
DoYourThingInternal();
log.Info("All done");
}
catch (MyException ex)
{
log.Error("Something went wrong here!");
throw;
}
}
protected abstract void DoYourThingInternal();
}
现在,有很多关于如何测试Abs
课程的信息,并确保调用DoYourThingInternal
。
但是,假设我想测试我的Conc
类:
public class Conc : Abs
{
protected override void DoYourThingInternal()
{
// Lots of awesome stuff here!
}
}
我不想做conc.DoYourThing()
,因为这将调用已经单独测试的父方法。
我想只测试重写方法。
有什么想法吗?
答案 0 :(得分:5)
您已将问题标记为“tdd”,但我怀疑您在遇到此“问题”时是否遵循了该原则。
如果你真的关注tdd你的工作流程就像是
在“6”中,您可能认为实现模板方法是个好主意,因为Conc1和Conc2共享一些逻辑。只需这样做,并运行测试,看看逻辑仍然有效。
编写测试来验证逻辑,不要基于它们实现的样子(=从测试开始)。在这种情况下,开始编写测试验证逻辑是否正常工作(逻辑后来放在您的具体类型中)。是的,这意味着一些代码行(抽象类中的代码行)会被多次测试。但那又怎么样?编写测试的一个重点是,您应该能够重构代码,但仍然能够通过运行测试来验证它是否有效。如果您以后不想使用模板方法模式,那么在理想的世界中,您不需要更改任何测试,只需更改实现。
如果你开始考虑你测试的代码行,IMO你可以完全放弃编写测试的好处。您希望确保您的逻辑正常工作 - 而不是为此编写测试。
答案 1 :(得分:3)
我承担了部分问题'是没有办法从类外调用受保护的方法。如何从Conc
派生并提供新的公共方法的模拟类:
public class MockConc: Conc
{
void MockYourThingInternal()
{
DoYourThingInternal()
}
}
答案 2 :(得分:2)
我不认为DoYourThingInternal()
与DoYourThing()
是分开的(例如,可以单独测试的两个独立代码模块),因为您将无法实例化您的抽象类无论如何,这两种方法总是一起运行。此外,DoYourThingInternal()
可以访问您班级中所有受保护的成员,并且可以对其进行修改,并对DoYourThing()
产生潜在的副作用。因此,我认为将DoYourThing()
与DoYourThingInternal()
的具体实现完全隔离是很危险的。
然而,这并不意味着你不能对DoYourThing()
的预期行为进行单独的测试,这些测试必须在所有Abs的实现中保持相同,并且DoYourThingInternal()
的预期行为
您可以使用(抽象)基础测试类来定义DoYourThing()
期望的一般行为的测试。然后创建与Abs的实现一样多的测试子类,并对每个实现的细节进行单元测试。
将继承基础测试类的测试,当您运行任何子类的测试时,DoYourThing()
的继承测试也将运行:
public abstract class AbsBaseTest
{
public abstract Abs GetAbs();
[Test]
public void TestSharedBehavior()
{
getAbs().DoYourThing();
// Test shared behavior here...
}
}
[TestFixture]
public class AbsImplTest : AbsBaseTest
{
public override Abs GetAbs()
{
return new AbsImpl();
}
[Test]
public void TestParticularBehavior()
{
getAbs().DoYourThing();
// Test specific behavior here
}
}
请参阅http://hotgazpacho.org/2010/09/testing-pattern-factory-method-on-base-test-class/
不知道所有单元测试框架是否支持抽象测试类继承(我认为NUnit确实如此)。
答案 3 :(得分:0)
如何在Abs上粘贴界面并嘲笑它?忽略这些电话,或对它们设定期望?
答案 4 :(得分:0)
你可以采取多种方式,其中许多方法已在此处记录。以下是我通常采用的方法:让测试用例继承自具体类。
public ConcTest : Conc
{
[Test]
public void DoesItsThingRight()
{
var Result = DoItsThingInternal();
// Assert the response
}
}
希望有所帮助!
布兰登