NUnit的TestCaseAttribute可以简化大量的测试参数输入用例的编写,如果基于Visual Studio Unit Test Project开发则默认没有类似的功能,看一段对比代码:

public class MyClass
{
public Int32 DoWork(String name, Int32 n)
{
if (String.IsNullOrWhiteSpace(name))
throw new ArgumentOutOfRangeException("name"); if (n < )
throw new ArgumentOutOfRangeException("n"); return name.Length / n;
}
}
[TestClass]
public class MyClassTest
{
[TestMethod]
public void DoWork()
{
var name = "test";
var n = ; var myClass = new MyClass();
var result = myClass.DoWork(name, n); Assert.IsTrue(result == name.Length / n);
} [TestMethod, ExpectedException(typeof(ArgumentOutOfRangeException))]
public void DoWork_NameIsNull()
{
var n = ; var myClass = new MyClass();
myClass.DoWork(null, n);
} [TestMethod, ExpectedException(typeof(ArgumentOutOfRangeException))]
public void DoWork_NameIsEmpty()
{
var n = ; var myClass = new MyClass();
myClass.DoWork(String.Empty, n);
} [TestMethod, ExpectedException(typeof(ArgumentOutOfRangeException))]
public void DoWork_NameIsWhiteSpace()
{
var n = ; var myClass = new MyClass();
myClass.DoWork(" ", n);
} [TestMethod, ExpectedException(typeof(ArgumentOutOfRangeException))]
public void DoWork_NLessThanZero()
{
var name = "test"; var myClass = new MyClass();
myClass.DoWork(name, -);
}
}

可以发现为了测试参数输入验证是否达到预期的效果,额外编写了4个测试用例。如果使用NUnit的TestCase可以简化如下:

[TestFixture]
public class MyClassTest
{
[TestCase("Test", )]
[TestCase(null, , ExpectedException = typeof(ArgumentOutOfRangeException))]
[TestCase("", , ExpectedException = typeof(ArgumentOutOfRangeException))]
[TestCase(" ", , ExpectedException = typeof(ArgumentOutOfRangeException))]
[TestCase("Test", -, ExpectedException = typeof(ArgumentOutOfRangeException))]
public void DoWork(String name, Int32 n)
{
var myClass = new MyClass();
var result = myClass.DoWork(name, n); Assert.IsTrue(result == name.Length / n);
}
}

要让Visual Studio Test支持类似的方式可以自己扩展,参考Visual Studio Team Test的Extending the Visual Studio Unit Test Type文章。不过我选择了更为简单的在原有的用例中扩展一个TestMethodCaseAttribute,例如:

[TestClass]
public class MyClassTest
{
[TestMethod]
[TestMethodCase("Test", )]
[TestMethodCase(null, , ExpectedException = typeof(ArgumentOutOfRangeException))]
[TestMethodCase("", , ExpectedException = typeof(ArgumentOutOfRangeException))]
[TestMethodCase(" ", , ExpectedException = typeof(ArgumentOutOfRangeException))]
[TestMethodCase("Test", -, ExpectedException = typeof(ArgumentOutOfRangeException))]
public void DoWork()
{
TestMethodCaseHelper.Run(context =>
{
var name = context.GetArgument<String>();
var n = context.GetArgument<Int32>(); var myClass = new MyClass();
var result = myClass.DoWork(name, n); Assert.IsTrue(result == name.Length / n);
});
}
}

只要有一个TestMethodCase未通过,当前的TestMethod既为失败。使用这种方式进行Code Coverage统计并不受影响,可以正确评估

public static class TestMethodCaseHelper
{
public static void Run(Action<TestMethodCaseContext> body)
{
var testMethodCases = FindTestMethodCaseByCallingContext(); foreach (var testMethodCase in testMethodCases)
RunTest(testMethodCase, body);
} internal static IEnumerable<TestMethodCaseAttribute> FindTestMethodCaseByCallingContext()
{
var stackFrames = StackFrameHelper.GetCurrentCallStack();
var forTestFrame = stackFrames.FirstOrDefault(p => GetTestMethodCaseAttributes(p).Any()); return forTestFrame != null ? GetTestMethodCaseAttributes(forTestFrame) : new TestMethodCaseAttribute[];
} private static IEnumerable<TestMethodCaseAttribute> GetTestMethodCaseAttributes(StackFrame stackFrame)
{
return GetTestMethodCaseAttributes(stackFrame.GetMethod());
} private static IEnumerable<TestMethodCaseAttribute> GetTestMethodCaseAttributes(MethodBase method)
{
return method.GetCustomAttributes(typeof(TestMethodCaseAttribute), true).OfType<TestMethodCaseAttribute>();
} private static void RunTest(TestMethodCaseAttribute testMethodCase, Action<TestMethodCaseContext> body)
{
TestSettings.Output.WriteLine("Run TestMethodCase {0} started", testMethodCase.Name);
var stopwatch = Stopwatch.StartNew();
RunTestCore(testMethodCase, body);
stopwatch.Stop();
TestSettings.Output.WriteLine("Run TestMethodCase {0} finished({1})", testMethodCase.Name, stopwatch.ElapsedMilliseconds);
} private static void RunTestCore(TestMethodCaseAttribute testMethodCase, Action<TestMethodCaseContext> body)
{
var testContext = new TestMethodCaseContext(testMethodCase); if (testMethodCase.ExpectedException != null)
RunTestWithExpectedException(testMethodCase.ExpectedException, () => body(testContext));
else
body(testContext);
} private static void RunTestWithExpectedException(Type expectedExceptionType, Action body)
{
try
{
body();
}
catch (Exception ex)
{
if (ex.GetType() == expectedExceptionType)
return; throw;
}
}
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public sealed class TestMethodCaseAttribute : Attribute
{
public TestMethodCaseAttribute(params Object[] arguments)
{
this.Arguments = arguments;
} public String Name { get; set; } public Type ExpectedException { get; set; } public Object[] Arguments { get; private set; }
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public sealed class TestMethodCaseAttribute : Attribute
{
public TestMethodCaseAttribute(params Object[] arguments)
{
this.Arguments = arguments;
} public String Name { get; set; } public Type ExpectedException { get; set; } public Object[] Arguments { get; private set; }
}
public class TestMethodCaseContext
{
private readonly TestMethodCaseAttribute _testMethodCase; internal TestMethodCaseContext(TestMethodCaseAttribute testMethodCase)
{
_testMethodCase = testMethodCase;
} public T GetArgument<T>(Int32 index)
{
return (T)_testMethodCase.Arguments.ElementAtOrDefault(index);
}
}
internal static class StackFrameHelper
{
public static IEnumerable<StackFrame> GetCurrentCallStack()
{
var frameIndex = ; while (true)
{
var stackFrame = new StackFrame(frameIndex, false); if (stackFrame.GetILOffset() == StackFrame.OFFSET_UNKNOWN)
break; yield return stackFrame; ++frameIndex;
}
}
}

简易扩展Visual Studio UnitTesting支持TestMethodCase的更多相关文章

  1. 如何扩展 Visual Studio 编辑器

    在 Visual Studio 2010 的时代,扩展 Visual Studio 的途径有很多,开发者可以选择宏.Add-in.MEF 和 VSPackages 进行自定义的扩展.但是宏在 Visu ...

  2. VS2015提示:未安装Style的Visual Studio语言支持,代码编辑Intellisense将不可用。服务器控件的标记Intellisense可能不起作用

    一.问题 最近在VS2015打开文件,提示未安装Style的Visual Studio语言支持,代码编辑Intellisense将不可用.服务器控件的标记Intellisense可能不起作用. Int ...

  3. 让Visual Studio x64 支持 __asm内联汇编

    目录 让Visual Studio x64 支持 __asm内联汇编 Intel Parallel Studio XE 2016安装 设置Interl C++ Compiler 使VS x64支持内联 ...

  4. 分享:扩展Visual Studio 的简单方法

    作为 MS 阵营的码农,相信Visual Studio 肯定是大家的主要武器了,但不知道大家有没有扩展Visual Studio 的需求. 最近我需要做一个工具,发现最好是实现在VS里面,于是,Goo ...

  5. .NET 开源了,Visual Studio 开始支持 Android 和 iOS 程序编写并自带 Android 模拟器

    .NET 开源了,Visual Studio 开始支持 Android 和 iOS 程序编写并自带 Android 模拟器 北京时间今天凌晨的 Connect(); 大会上,多少程序员的假想成为现实. ...

  6. 简介Gulp, Grunt, Bower, 和 Npm 对Visual Studio的支持

    [原文发表地址]Introducing Gulp, Grunt, Bower, and npm support for Visual Studio Web 开发,特别是前端 Web 开发,正迅速变得像 ...

  7. [转]简介Gulp, Grunt, Bower, 和 Npm 对Visual Studio的支持

    本文转自:http://www.cnblogs.com/whitewolf/p/4009199.html [原文发表地址]Introducing Gulp, Grunt, Bower, and npm ...

  8. Visual Studio 2013支持Xamarin的解决方案

    转自博客园[遗忘的代码] Xamarin的Visual Studio插件目前还不支持VS 2013,所以需要在安装Xamarin的VS插件时把2010和2012全选上 (由于我的电脑里只剩2013,而 ...

  9. Visual Studio 2013 支持MVC3不完善,Razor智能提示不完整或者不提示

    以下只是针对MVC3. 前天试用Orchard 1.8,用VS2013新建C#类库项目(ClassLibrary project),然后新建Views文件夹,新建cshtml,然后引用MVC3的相关d ...

随机推荐

  1. 3.7 su命令 3.8 sudo命令 3.9 限制root远程登录

    3.7 su命令 3.8 sudo命令 3.9 限制root远程登录 su命令 切换用户 [root@centos_1 ~]# su - xiaobo [root@centos_1 ~]# su - ...

  2. GetDlgItem的用法小结

    GetDlgItem用于获得指定控件ID的窗体指针,函数原型如下: HWND GetDlgItem( HWND hDlg, int nIDDlgItem ); CWnd* GetDlgItem(int ...

  3. [转]Linux内核源码详解--iostat

    Linux内核源码详解——命令篇之iostat 转自:http://www.cnblogs.com/york-hust/p/4846497.html 本文主要分析了Linux的iostat命令的源码, ...

  4. iis重启的几种方法

    1. 通过“IIS管理器”重启在IIS服务器管理控制树中展开IIS节点,选择需要重新启动IIS服务的计算机,接着单击鼠标右键,选择“所有任务”->“重新启动IIS”. 2.通过“控制面板”-&g ...

  5. sed在替换的时候,使用变量中的值?如何在sed实现变量的替换?获取到变量中的值?

    需求描述: 今天在做nrpe配置的时候,想要通过批量的方式来将定义文件中的IP给替换掉 开始做的时候没有成功,报错了.在此记录下,如何实现,获取到变量的值,然后 进行替换. 操作过程: 1.原文件的内 ...

  6. linux环境中,read命令的使用?

    需求描述: 今天有一个需求主要是从一个文件中夺取内容,文件中每行呢又是有多个字段,中间通过空格来分隔的.那么这样的话,就是用到了read命令 在这里记录下read的使用方法. 命令说明:read命令就 ...

  7. CentOS 7 mini安装后安装图形界面及远程设置

    安装图形界面 yum group install "GNOME Desktop" "Graphical Administration Tools" 安装 xrd ...

  8. [SLAM] Little about SLAM

    Books from Zhihu: 幽默一把 看完Gonzalez:嗯,好像很好玩的样子,我也来搞一搞.看完Price:什么鬼,怎么这么多公式,公式看不懂肿么破.看完Szeliski:原来用一千页的书 ...

  9. VS2010安装包制作全过程图解

    项目的第一个版本出来了,要做个安装包,很久没做过已经有些淡忘了,网上差了差资料,写了一个,总结下,可能还不是很完善,仅作参考. 1.首先在打开 VS2010    >新建>项目 2.创建一 ...

  10. Android学习——在Android中使用OpenCV的第一个程序

    刚開始学习Android,因为之前比較熟悉OpenCV,于是就想先在Android上执行OpenCV试试 =============================================== ...