J. *_* Ed 7 language-agnostic tdd unit-testing
假设以下模板方法实现:
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();
}
Run Code Online (Sandbox Code Playgroud)
现在,有很多关于如何测试Abs类的信息,并确保DoYourThingInternal调用它.
但是,假设我想测试我的Conc课程:
public class Conc : Abs
{
protected override void DoYourThingInternal()
{
// Lots of awesome stuff here!
}
}
Run Code Online (Sandbox Code Playgroud)
我不想这样做conc.DoYourThing(),因为这将调用已经单独测试的父方法.
我想只测试重写方法.
有任何想法吗?
你已经标记了问题"tdd",但是当你遇到这个"问题"时我怀疑你是否遵循了这个原则.
如果你真的关注tdd你的工作流程会是这样的
在"6"中,您可能认为实现模板方法是个好主意,因为Conc1和Conc2共享一些逻辑.只需这样做,并运行您的测试,看看逻辑仍然有效.
编写测试来验证逻辑,不要以实现的方式为基础(=从测试开始).在这种情况下,开始编写测试验证逻辑是否正常工作(逻辑后来放在您的具体类型中).是的,这意味着一些代码行(抽象类中的代码行)会被多次测试.但那又怎么样?编写测试的一个重点是,您应该能够重构代码,但仍然能够通过运行测试来验证它是否有效.如果您以后不想使用模板方法模式,那么在理想的世界中,您不需要更改任何测试,只需更改实现即可.
如果你开始考虑你测试的代码行,IMO你可以完全放弃编写测试的好处.您希望确保您的逻辑正常工作 - 为此编写测试.
我不会考虑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
}
}
Run Code Online (Sandbox Code Playgroud)
请参阅http://hotgazpacho.org/2010/09/testing-pattern-factory-method-on-base-test-class/
不知道所有单元测试框架是否都支持抽象测试类继承(我认为 NUnit 支持)。