简化C#版Junit
前言
这是整理至 麦库 上的一个我造的简单轮子
今日查看了Junit 3.8的部分实现, 决定利用C# 模仿Java上 著名Junit 框架。
实现要求:
给定一些用户定义的类,运行其中 public 、void 、无参数的方法。
实现:
TestCase
面向用户的接口,该类中包含了:一个Test()公共方法。该方法调用其指定的实例方法(即单元测试方法)。
public abstract class TestCase { public string TestMethodName { get; set; } public void Test() { this.BaseTest(); } /// <summary> /// 每个 TestCase 的实例 对象,将会运行根据TestMethodName 指定的方法。 /// 如此设计可以使, 每个测试方法对应一个实例。 一个类中如果有多个测试方法那么每个测试方法各自对一个实例对象互不干扰。 /// 如果只采用一个实例,会因为类某个方法抛出异常 而无法继续测试该类的其他方法。 /// </summary> protected void BaseTest() { MethodInfo method = this.GetType().GetMethod(TestMethodName, new Type[] { }); this.BeforeTest(); try { method.Invoke(this, null); Console.WriteLine("方法测试成功"); } catch (Exception ex) { Console.WriteLine("失败!{0}", ex.Message); } finally { this.AfterTest(); } } public abstract void BeforeTest(); public abstract void AfterTest(); }
TestRunner
测试运行人,
该类中有一个
public void AddTestCase(Type test)
方法 提供给客户端,用于指定用户 需要测试哪些类。
public class TestRuner { public TestRuner() { testCases = new List<Type>(); tests = new List<TestCase>(); } private List<Type> testCases; private List<TestCase> tests; public long Milles { get;private set; } public void AddTestCase(Type test) { testCases.Add(test); } public void Run() { this.BaseRun(); } private void BaseRun() { buildTest(); Stopwatch timer = new Stopwatch(); timer.Start(); foreach (var item in tests) { item.Test(); } timer.Stop(); this.Milles = timer.ElapsedMilliseconds; } /// <summary> /// 构建测试,扫描所有 指定需要测试的 类型的方法, /// </summary> private void buildTest() { //testCases 为 Type foreach (var item in testCases) { foreach (var method in item.GetMethods()) { addTestMethod(method, item); } } } private void addTestMethod(MethodInfo method, Type testClass) { if (isTestMethod(method)) { addTest(createTest(method, testClass)); } } private bool isTestMethod(MethodInfo method) { ParameterInfo[] parames = method.GetParameters(); Type returnType = method.ReturnType; return method.Name.StartsWith("test") && parames.Length == 0 && returnType ==typeof(void); } /// <summary> /// 如果发现符合要求的测试方法,则创建一个TestCase 实例对象,并制定该对象需要调用的Test方法的方法名。 /// </summary> /// <param name="method"></param> /// <param name="testClass"></param> /// <returns></returns> private TestCase createTest(MethodInfo method, Type testClass) { ConstructorInfo constructorInfo = testClass.GetConstructor(new Type[]{}); TestCase testCase = constructorInfo.Invoke(null) as TestCase; testCase.TestMethodName = method.Name; return testCase; } private void addTest(TestCase test) { tests.Add(test); } }
由上面两个类可以发现:
TestRunner 中类用反射 来调用需要测试的方法(实际上 真正的单元测试方法的调用是交给了 TestCase 实例对象 自己执行的。TestRunner 只负责制定方法)。
TestCase 类承担了 :为用户的单元测试提供接口,并且实际的运行具体的单元测试(TestBase 采用了 适配器模式 adapter。 因为 TestRunner无法知道 用户所编写的单元测试 的方法的名字,即无法进行直接的调用。所以需要使用适配器模式。 利用 TestCase 中的 TestBase方法做一个转接,将接口转向 Test,提供给TestRunner 调用)。
客户端:
static void Main(string[] args) { TestRuner run = new TestRuner(); run.AddTestCase(typeof(Classdafgsadfsafqqwwfqs2)); run.AddTestCase(typeof(Class1)); Console.WriteLine("开始测试"); run.Run(); Console.WriteLine("测试结束,耗时:{0}", run.Milles); Console.Read(); }
延伸任务:
1、利用attribute 标签来实现上面需求
2、利用Java重新模拟
3、完成研究Junit 框架代码(复刻其代码框架)
4、查看 Nunit的设计。
参考链接:
https://github.com/KentBeck/junit