简易扩展Visual Studio UnitTesting支持TestMethodCase
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的更多相关文章
- 如何扩展 Visual Studio 编辑器
在 Visual Studio 2010 的时代,扩展 Visual Studio 的途径有很多,开发者可以选择宏.Add-in.MEF 和 VSPackages 进行自定义的扩展.但是宏在 Visu ...
- VS2015提示:未安装Style的Visual Studio语言支持,代码编辑Intellisense将不可用。服务器控件的标记Intellisense可能不起作用
一.问题 最近在VS2015打开文件,提示未安装Style的Visual Studio语言支持,代码编辑Intellisense将不可用.服务器控件的标记Intellisense可能不起作用. Int ...
- 让Visual Studio x64 支持 __asm内联汇编
目录 让Visual Studio x64 支持 __asm内联汇编 Intel Parallel Studio XE 2016安装 设置Interl C++ Compiler 使VS x64支持内联 ...
- 分享:扩展Visual Studio 的简单方法
作为 MS 阵营的码农,相信Visual Studio 肯定是大家的主要武器了,但不知道大家有没有扩展Visual Studio 的需求. 最近我需要做一个工具,发现最好是实现在VS里面,于是,Goo ...
- .NET 开源了,Visual Studio 开始支持 Android 和 iOS 程序编写并自带 Android 模拟器
.NET 开源了,Visual Studio 开始支持 Android 和 iOS 程序编写并自带 Android 模拟器 北京时间今天凌晨的 Connect(); 大会上,多少程序员的假想成为现实. ...
- 简介Gulp, Grunt, Bower, 和 Npm 对Visual Studio的支持
[原文发表地址]Introducing Gulp, Grunt, Bower, and npm support for Visual Studio Web 开发,特别是前端 Web 开发,正迅速变得像 ...
- [转]简介Gulp, Grunt, Bower, 和 Npm 对Visual Studio的支持
本文转自:http://www.cnblogs.com/whitewolf/p/4009199.html [原文发表地址]Introducing Gulp, Grunt, Bower, and npm ...
- Visual Studio 2013支持Xamarin的解决方案
转自博客园[遗忘的代码] Xamarin的Visual Studio插件目前还不支持VS 2013,所以需要在安装Xamarin的VS插件时把2010和2012全选上 (由于我的电脑里只剩2013,而 ...
- Visual Studio 2013 支持MVC3不完善,Razor智能提示不完整或者不提示
以下只是针对MVC3. 前天试用Orchard 1.8,用VS2013新建C#类库项目(ClassLibrary project),然后新建Views文件夹,新建cshtml,然后引用MVC3的相关d ...
随机推荐
- DWZ主从表界面唯一性验证(后台验证)(三)
之前的博客介绍了前台自写js来验证主动表的唯一性,除了前台的验证,我也学习了后台的一些判断. 再次介绍一下背景需求: 利用DWZ的主从表结构批量添加课程信息,在提交表单后,触发Action事件 1.是 ...
- MVC Controller向View传递数据
ASP.NET MVC中,Controller向View传递数据的方式有一下6种 ViewData ViewBag PartialView TempData ViewModel Tuple 1.Vie ...
- [转]iOS证书(.p12)和描述文件(.mobileprovision)申请
转载于:http://ask.dcloud.net.cn/article/152 iOS有两种证书和描述文件: 证书类型 使用场景 开发(Development)证书和描述文件 用于开发测试,在HBu ...
- Uploading File using Ajax and receiving binary data in Asp.net (C#)[转]
基础知识,可由此衍生.原文:http://uniapple.net/blog/?p=2050 In this post, I will show you how to upload a file us ...
- fitnesse页面增加认证
一.增加用户认证1. 只增加一个认证用户:java -jar fitnesse.jar -a username:password 2. 增加多个认证用户(明文密码) 2.1 新建一个passwo ...
- Linux安装rpc监控系统资源
1.rpc服务需rsh的支持,一般情况下rsh已安装.rpm -qa rsh查看. 2.右键另存为http://heanet.dl.sourceforge.net/sourceforge/rstatd ...
- 【git】git pull
http://www.01happy.com/git-resolve-conflicts/
- javascript报错集锦
1.JS 异常之 missing ) after argument list 错误释疑报错原因:不是字符串就输出啦
- 基于SpringBoot搭建应用开发框架(一) —— 基础架构
目录 Spring的简史 零.开发技术简介 一.创建项目 1.创建工程 2.创建Starter 3.启动项目 4.Spring Boot 配置 5.项目结构划分 二.基础结构功能 1.web支持 2. ...
- Linux系统安装workerman,启动wss 服务
安装workerman其实很简单,只要会简单的linux口令就可以搞定, 这里我给大家演示一下如何安装workerman 进入终端的过程就不用演示了吧... 输入root及密码进入终端后找到站点根目录 ...