之前的项目中做单元测试一直用的是NUnit,这次做新项目,负责人要求统一用MsTest,理由是MsTest是Visual Studio内置的。用就用吧,我没什么意见。不过用了两天,我就发现一个大问题:MsTest并不支持参数化测试(也有叫行测试的)。

什么是参数化测试?简单的说,就是同样的逻辑,根据输入参数不同给出不同的结果。因为只是参数不同,所以并不希望把测试写多遍,但是又希望对每个参数的测试成为一个独立的测试用例。举例说,假定我有一个数学计算的方法是把数字*2,我希望证明这个方法对于正数、负数和0都是通过的。在NUnit中可以通过TestCaseAttribute来为测试提供参数:

[TestCase(, )]
[TestCase(, )]
[TestCase(-, -)]
public void TestDouble(int value, int result)
{
Assert.AreEqual(result, Calc.Double(value));
}

但是MsTest中并没有类似功能的Attribute。怎么办呢?最简单粗暴的方法当然是写一个循环提供参数了。不过这并不是我想要的结果,一是写循环遍历的代码有点无谓;更重要的是循环只能作为一个整体的测试用例来执行,如果出错的话不自己写信息就无法分辨出到底哪个参数出错了。

网上找了找,发现问类似问题的人还真不少。基本上回答集中在下面几种方案:

1. VS2012 Update1以后提供了DataRow属性,但是只能在Windows Store Apps使用。(个人觉得这一点好奇葩,一个测试功能还有必要专门优待 Store用户吗?)总之我们做的是普通类库,这个法子不适用。

2. 用DataSourceAttribute,按照文档这个标注可以提供OleDB数据源、XML或者CSV格式的文件作为数据源。对于简单的参数测试这个方案实在有点太重了,再说按照定义使用了外部文件或数据库的单元测试还能算是单元测试吗?

3. 用PostSharp增强代码的方法来扩展测试。该方案我大概看了一下也放弃了,毕竟PostSharp是比较非主流的技术,再说使用它会明显增加编译过程的复杂性,对持续集成也有点不友好。

4. 按照MsTest对象模型来写一些扩展属性。老实说在寻找解决方案之前我已经心里判断应该会走这条路,而且按照网络的示例真的写了几个扩展属性出来测试,编译倒是正常,但测试就是死活运行不起来。再仔细看说明,原来要在Visual Studio运行扩展测试还要修改几个注册表项....好吧我们开发组十几号人都去折腾注册表的话才能测试的话,还让不让小伙伴们开心的一起玩耍了。总之这个方案也Pass。

看来看去,就没什么令人满意的办法,唯一还算可行的只有用DataSource了。当然我是不会用数据库这么重量级的数据源的,那对单元测试来说完全是高射炮打蚊子。用文件的话,从存粹的敏捷开发理念来说也是有问题的,但文件毕竟还算相对容易控制。于是我用XML文件测试了一下,通过了,但是有几个小坑需要注意,这里记录一下。

代码示例如下,很简单,不解释了:

[TestClass]
[DeploymentItem(@"Fixtures\add.xml")]
public class UnitTest1
{
public TestContext TestContext { get; set; } [TestMethod]
[DataSource("Microsoft.VisualStudio.TestTools.DataSource.XML",
@"|DataDirectory|\Fixtures\add.xml",
"row",
DataAccessMethod.Sequential)]
public void TestMethod1()
{
int a = Convert.ToInt32(TestContext.DataRow["a"]);
int b = Convert.ToInt32(TestContext.DataRow["b"]);
int expected = Convert.ToInt32(TestContext.DataRow["expected"]);
var result = new Class1().Add(a, b);
Assert.AreEqual(expected, result);
}
}

测试用的XML文件:

<rows>
<row a="1" b="2" expected="3" />
<row a="2" b="-1" expected="-1" />
<row a="3" b="0" expected="3" />
</rows>

对测试有几点需要注意:

  1. 测试类必须加一个TestContext属性;
  2. XML文件编译属性的 Copy to output directory 应该设置为 Copy always 或者 Copy if newer;
  3. 要在远程服务器上测试成功(比如CI服务器),需要加DeployItem标注,表示这个文件要和程序集一起部署到测试位置;
  4. DataSource的第二个参数里DataDirectory是指什么,MSDN里只拿数据库为例子,没有对XML/CSV的情况做说明。从结果看,应该就是程序输出的位置。

结果是可用的,但是老实说,这样的测试代码从观感上来说远不如NUnit那么简单优雅。我觉得微软与其把Visual Studio的测试界面搞得那么复杂,还不如给MsTest增加点实用的核心功能。当然这些是题外话了。

MSTest不支持参数化测试的解决方案的更多相关文章

  1. 参数化测试与Mock

    参数化测试与Mock 转载自https://blog.csdn.net/sunliduan/article/details/42026509 单元测试概念 说到测试,大家都不会陌生,从我们开始学习编程 ...

  2. Python 中如何实现参数化测试?

    Python 中如何实现参数化测试? 之前,我曾转过一个单元测试框架系列的文章,里面介绍了 unittest.nose/nose2 与 pytest 这三个最受人欢迎的 Python 测试框架. 本文 ...

  3. Google C++单元测试框架GoogleTest---值参数化测试

    值参数化测试允许您使用不同的参数测试代码,而无需编写同一测试的多个副本. 假设您为代码编写测试,然后意识到您的代码受到布尔参数的影响. TEST(MyCodeTest, TestFoo) { // A ...

  4. testng入门教程10 TestNG参数化测试

    在TestNG的另一个有趣的功能是参数测试.在大多数情况下,你会遇到这样一个场景,业务逻辑需要一个巨大的不同数量的测试.参数测试,允许开发人员运行同样的测试,一遍又一遍使用不同的值. TestNG让你 ...

  5. JUnit之参数化测试、套件/成组测试的使用

    原文地址http://blog.csdn.net/yqj2065/article/details/39967065 参数化测试 正如数组替代int a0,a1,a2一样,测试加法时assertEqua ...

  6. Junit5中实现参数化测试

    从Junit5开始,对参数化测试支持进行了大幅度的改进和提升.下面我们就一起来详细看看Junit5参数化测试的方法. 部署和依赖 和Junit4相比,Junit5框架更多在向测试平台演进.其核心组成也 ...

  7. JUnit5学习之六:参数化测试(Parameterized Tests)基础

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  8. JUnit5学习之七:参数化测试(Parameterized Tests)进阶

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  9. JUnit5参数化测试的几种方式

    参数化测试一直是津津乐道的话题,我们都知道JMeter有四种参数化方式:用户自定义变量.用户参数.CSV文件.函数助手,那么JUnit5有哪些参数化测试的方式呢? 依赖 JUnit5需要添加junit ...

随机推荐

  1. 如何在C#中循环一个枚举

      在C#中要想迭代循环一个枚举,最容易想到的办法是直接进行循环,如下代码所示:   public enum Suit { Spades, Hearts, Clubs, Diamonds } publ ...

  2. SQL中N $ # @的作用

    declare @sql nvarchar(4000) set @sql= N'select @TotalRecords=count(*) from ' + N'(' + @sqlFullPopula ...

  3. Linux系统故障处理案例(一)

    运行环境:CentOS6.7 故障原因: 昨天在线执行命令yum -y update 在命令执行途中,强制中断并直接运行poweroff命令关机.再次开机出现如图所示故障指示: 根据提示信息分析,可能 ...

  4. [转]Oracle ORA-01403: no data found Exception SYS_REFCURSOR

    本文转自:http://stackoverflow.com/questions/9104153/what-is-the-correct-way-to-deal-with-this-oracle-ora ...

  5. 关于Eclipse中的快捷键占用的解决.

    刚进公司用的之前离职员工的电脑,打开Eclipse经常用的一个alt+/ 内容提示快捷键就是不好使. 让同事帮忙之后才发现原因. 在eclipse中快捷键设置是在  windows---->pr ...

  6. Redis主备复制

    Redis 支持 Master-Slave(主从)模式,Redis Server 可以设置为另一个 Redis Server 的主机(从机),从机定期从主机拿数据.特殊的,一个从机同样可以设置为一个 ...

  7. js运动

    一.offsetWidth / offsetHeight 获取整个块的宽度/高度,包括border 二.clientWidth / clientHeight 获取块的宽度/高度,不包括border 三 ...

  8. 20160512--hibernate--缓存

    缓存 缓存的作用主要用来提高性能,可以简单的理解成一个Map:使用缓存涉及到三个操作:把数据放入缓存.从缓存中获取数据.删除缓存中的无效数据.   原理模拟分析:(不能运行,只是模拟)(缓存实现复杂, ...

  9. 20150503-struts2入门-标签

    一.几个标签介绍 1.property标签 property标签用于输出指定值: <s:set name="name" value="'kk'" /> ...

  10. 【JAVA】浅谈java内部类

    一.什么是内部类? 到底什么是内部类呢?通俗的讲,就是在类内部定义的类,包括定义在一个类的方法外面.方法里面或者代码块中. 二.为什么要使用内部类? 为什么我们要不走寻常路,把一个类定义在另一个类的内 ...