网络上可以搜索到很多TDD的文章,但是很大一部分只是讲述怎样使用NUnit等工具的使用。只有切身的去体会TDD的每一个环节,才能真正理解TDD。
工具:NUnit的原理
NUnit的原理很简单,是新建一个TestFixture实例,然后依次调用TestFixture中的Test Case,然后纪录Test Case的运行结果 NUnit的核心对象是TestCase, TestSuit, TestResult
TestCase指一个Test Case,比如一个[Test]属性标记的方法 TestSuit指一组Test Case,比如一个[TestFixture]属性标记的对象 TestResult指Test Case运行的结果,TestResult是一个抽象类,在NUnit中,有两个类是继承自TestResult的:TestCaseResult和TestSuiteResult
NUnit是怎样运行Test Case的
NUnit定义了一个处理Test Case的抽象类TestCase TestCase类重要的方法是Run()
public override TestResult Run(EventListener listener) public abstract void Run(TestCaseResult result);
TestCase运行的结果会存入一个TestCaseResult对象
调用Run方法如果传入实现了EventListener接口的对象话,可以在TestCase实际运行之前以及TestCase运行之后进行自定义的处理 …… listener.TestStarted(this); Run(testResult); listener.TestFinished(testResult); ……
NUnit还定义了一个实现抽象类TestCase的通用模版 public abstract class TemplateTestCase : TestCase
TemplateTestCase中Run方法的基本框架为 public override void Run(TestCaseResult testResult ) { try { InvokeSetUp(); //设置环境 InvokeTestCase(); //运行Test Case InvokeTearDown(); //恢复环境 ProcessNoException(testResult); //无异常退出 } catch { ProcessException(testResult); //异常处理 } }
在Run方法中还会计算Test Case实际运行的时间和所用的内存 DateTime start = DateTime.Now; long before = System.GC.GetTotalMemory( true );
…. //run test case
long after = System.GC.GetTotalMemory( true ); testResult.Leakage = after – before; DateTime stop = DateTime.Now; TimeSpan span = stop.Subtract(start); testResult.Time = (double)span.Ticks / (double)TimeSpan.TicksPerSecond;
下面几个类都是继承自TestCase类或者TemplateTestCase类 NormalTestCase //一般的Test Case NotRunnableTestCase //不可运行的Test Case ExpectedExceptionTestCase //定了期望异常的Test Case
为什么写的Test Case没有自动运行 写Test Case时候要注意,Test Case必须是public的,无参数的,无返回值的函数 参考: public class NotRunnableTestCase : TestCase { public NotRunnableTestCase(MethodInfo method) : base(method.DeclaringType.FullName, method.Name) { string reason;
if (method.IsAbstract) reason = “it must not be abstract”; else if (method.IsStatic) reason = “it must be an instance method”; else if (!method.IsPublic) reason = “it must be a public method”; else if (method.GetParameters().Length != 0) reason = “it must not have parameters”; else if (!method.ReturnType.Equals(typeof(void))) reason = “it must return void”; else reason = “reason not known”;
ShouldRun = false; IgnoreReason = String.Format(“Method {0}’s signature is not correct: {1}.”, method.Name, reason); } }