用反射与自定义属性实现单元测试工具(NUnit)

反射是.Net、Java等托管语言区别于传统语言(C++,Delphi等)的一大特性,通过反射我们可以动态的创建类,调用方法等。考虑了一下NUnit的功能,觉得利用这个特性也可以做一个出来,于是就动手做了个练习,基本达到了效果,模拟NUnit提供框架如下:

//=======================================

UnitDll (模拟nunit.framework)

[AttributeUsage(AttributeTargets.Class)]
 public class TestFixtureAttribute : System.Attribute
 {
  public TestFixtureAttribute()
  {
  }
 }

[AttributeUsage(AttributeTargets.Method)]
 public class Test : System.Attribute
 {
  public Test()
  {
  }
 }

// 以上为两个Attribute,标识要测试的类与方法

 /// <summary>
 /// Assert的异常类,用来抛出Assert异常
 /// </summary>
 public class VAssertException : System.ApplicationException
 {
  
  private string m_expstr  = "";

  public VAssertException()
  {
  }

  public VAssertException(string err)
  {
     m_expstr = err;
  }

  public string ExceptionStr
  {
   get   {    return m_expstr;   }
   set   {    m_expstr = value;   }
  }
 }

// 以上为异常类,Assert失败的时候扔出异常

 /// <summary>
 /// 用于单元测试的Assert类
 /// </summary>
 public class Assert
 {
  public Assert()
  {
  }

  public static void AreEqual(object expected, object actual)
  {
 
   if (expected != actual)
   {
    string str = "expected is:"+expected+" but actual is:"+actual;
    
    VAssertException e = new VAssertException(str);
    throw e;
   }
  }
 }

// 以上为提供测试方法类,提供AreEqual,模拟NUnit提供的Assert类

//===========================================================

TestDll : 要进行单元测试的类,使用上面提供的UnitDll

/// <summary>
 /// // 要进行单元测试的类
 /// </summary>
 [TestFixture]                                 // 标识为要进行测试的类
 public class VUnitTest
 {
  public VUnitTest()
  {
   System.Console.WriteLine("Now the Unit Test Class create");
  }

  [Test]
  public void TestMethod()
  {
   System.Console.WriteLine("This is a unit test method");
   Assert.AreEqual(1,2);                                // 使用UnitDll提供的Assert类进行单元测试
  }

//==================================

UnitTest.exe   模拟NUnit.exe,提供的一个win form,主要以treeview的方式展现。

界面的代码比较简单就不提供了,提供几段关键的反射与调用的代码:

  private void LoadDll()
  {
   this.treeView1.Nodes.Clear();
   rootnode = this.treeView1.Nodes.Add(openFileDialog1.FileName);
     
   a = Assembly.LoadFrom(openFileDialog1.FileName);

   Type [] types = a.GetTypes();

   foreach (Type type in types)
   {
    object [] attributes = type.GetCustomAttributes(false);

    foreach (object obj in attributes)
    {
     // 得到了带有测试注明信息的类
     if (obj.GetType() == typeof(TestFixtureAttribute))
     {
      // 增加一个类名
      TreeNode node = rootnode.Nodes.Add(type.Name);

      MethodInfo [] methods = type.GetMethods();

      foreach (MethodInfo method in methods)
      {
       object [] methodattributes = method.GetCustomAttributes(false);

       foreach (object objmehtod in methodattributes)
       {
        if (objmehtod.GetType() == typeof(Test))
        {
         node.Nodes.Add(method.Name);
        }
       }

      }

     }

    }

   }

   this.treeView1.ExpandAll();

  }
// 上面的代码比较简单,利用反射得到需要进行测试的类与方法,并展现在一个treeview中

    object obj = Activator.CreateInstance(type);

    MethodInfo method = type.GetMethod(this.treeView1.SelectedNode.Text);
    if (method == null)
    {
     MessageBox.Show("方法不匹配");
     return;
    }

    object [] parameters = new object[0];
    method.Invoke(obj,parameters);

    this.listBox1.Items.Insert(0,"ok");
    SetLabel(true);

   }
   catch(System.Reflection.TargetInvocationException ex)
   {
    string str = ex.ToString();

    Exception exc = ex.InnerException;

    if (exc.GetType() == typeof(VAssertException))
    {
     VAssertException objex = (VAssertException)exc;

     string sstr = objex.ExceptionStr;
     this.listBox1.Items.Insert(0,sstr);

     this.listBox1.Items.Insert(0,exc.StackTrace);
     this.listBox1.Items.Insert(0,ex.StackTrace);

     SetLabel(false);

    }

// 上面的代码相当于NUnit界面中的run,也是比较核心的一段

// 主要就是通过创建要测试的类(CreateInstance),然后调用Invoke

// 判断是否断言失败(是否有自定义的异常扔出),有的话测试失败,把失败信息显示在界面即可

//======================================================

小结: 基本可以达到NUnit的效果,但是业务与界面的代码混在一起,没有很好的隔离,

比较好的方法是把反射这块封起来,提供一个接口给界面反应即可。

请大家多提意见,谢谢

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值