C# 单元测试(入门)

注:本文示例环境数组

VS2017
XUnit 2.2.0 单元测试框架
xunit.runner.visualstudio 2.2.0 测试运行工具
Moq 4.7.10 模拟框架网络

什么是单元测试?

确保软件应用程序按做者的指望执行操做,其中最好的一种方法是拥有自动化测试套件。 能够对软件应用程序进行各类不一样的测试,包括集成测试、Web 测试、负载测试等。 测试各个软件组件或方法的单元测试是最低级测试。框架

所谓单元测试(unit testing),就是开发者编写的一小段代码,用于对软件中的最小单元进行检查和验证,其通常验证对象是一个函数或者一个类。一般而言,一个单元测试是用于判断某个特定条件(或者场景)下某个特定函数的行为。编辑器

为何要使用单元测试?

  • 大大节约了测试和修改的时间,有效且便于测试各类状况。
  • 能快速定位bug(每个测试用例都是具备针对性)。
  • 能使开发人员从新审视需求和功能的设计(难以单元测试的代码,就须要从新设计)。
  • 强迫开发者以调用者而不是实现者的角度来设计代码,利于代码之间的解耦。
  • 自动化的单元测试能保证回归测试的有效执行。
  • 使代码能够放心修改和重构。
  • 测试用例,可做为开发文档使用(测试即文档)。
  • 测试用例永久保存,支持随时测试。

对于我我的来讲,主要是防止本身犯低级错误的,同时也方便修改(BUG修复)而不引入新的问题。能够放心大胆的重构。简言之,这个简单有效的技术就是为了令代码变得更加完美。ide

既然单元测试有这些好处,为何咱们不去用呢?

能够概括为如下几个理由。函数

  1. 对单元测试存在的误解,如:单元测试属于测试工做,应该由测试人员来完成,因此单元测试不属于开发人员的职责范围。工具

    答:虽然单元测试虽然叫作"测试",但实际属于开发范畴,应该由开发人员来作,而开发人员也能从中受益。单元测试

  2. 没有真正意识到单元测试的收益,认为写单元测试太费时,不值得。测试

    答:在开发时越早发现bug,就能节省更多的时间,下降更多的风险。单元测试先期要编写测试用例,是须要多耗费些时间,可是后面的调试、自测,均可以经过单元测试处理,不用手工一遍又一遍处理。实际上总时间被减小了。spa

  3. 项目经理或技术主管没有要求写单元测试,因此不用写。

    答:写单元测试应该成为开发人员的一种本能,开发自己就应该包含单元测试。

  4. 不知道有单元测试这回事,不知道如何用。通过这篇文档的说明,就基本知道如何处理单元测试。

框架选型

经常使用单元测试框架:MSTest (Visual Studio官方)、XUnit 和 NUnit。

  1. MS Test为微软产品,集成在Visual Studio 2008+工具中。
  2. NUnit为.Net开源测试框架(采用C#开发),普遍用于.Net平台的单元测试和回归测试中,官方网址(www.nunit.org)。
  3. XUnit.Net为NUnit的改进版。

(如下主要讲解MSTest 和NUnit的使用,XUnit操做和NUnit操做基本相似)

基础实践

开始建立你的第一个的单元测试项目吧

1)  咱们先来用 VS2017 中自带的测试模块(MSTest)来写一个简单的单元测试吧。
  1. 新建一个Solution,并添加项目UnitTestDemo(用于编写被测试的项目)

  2. 在该工程中添加UnitTestClass类,并书写一个静态的GetTriangle(string[] sideArr) 函数用来返回一个三角形的类型。

namespace UnitTest
{
    public class UnitTestClass
    {
        /// <summary>
        /// 获取三角形类型.
        /// </summary>
        /// <param name="sideArr">三角形三边长度数组.</param>
        /// <returns>返回三角形类型名称.</returns>
        public static string GetTriangle(string[] sideArr)
        {
            string result = string.Empty;
            int a = int.Parse(sideArr[0]);
            int b = int.Parse(sideArr[1]);
            int c = int.Parse(sideArr[2]);
            if (a + b > c && a + c > b && b + c > a)
            {
                if (a == b && a == c)
                {
                    result = "等边三角形";
                }

                if (a == b || a == c || b == c)
                {
                    result = "等腰三角形";
                }
                else
                {
                    result = "通常三角形";
                }
            }
            else
            {
                result = "不构成三角形";
            }
            return result;
        }
    }
}
  1. 而后在solution中添加一个UnitTestDemoTests测试项目,如图所示,添加 => 新建项目以后选择 测试 => 单元测试项目。

新建好测试项目以后,你会获得一个UnitTest1测试类模板,即一个带有[TestClass] attribute标记的类和一个带有[TestMethod] attribute标记的空方法public void TestMethod1()

  1. 单元测试项目没法自动访问它正在测试的类库。 能够经过添加对类库项目的引用来提供测试库访问权限。 为此,请右键单击UnitTestProject1项目,而后依次选择“添加” > “引用”。在“引用管理器”对话框中,而后选择 UnitTestDemo项目,以下图中所示。

    

  在UnitTestDemoTests项目中添加UnitTestDemo项目的引用,如今咱们的solution就具备了下图所示的目录结构。

    

  5. 在UnitTestDemoTests项目中的UnitTest1类中,将模板提供的样本单元测试代码替换为如下代码:

using UnitTest;
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace UnitTestDemoTests
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod()]
        public void GetTriangle_Test()
        {
            string[] sideArr = {"5", "5", "5"};
            Assert.AreEqual("等边三角形", UnitTestClass.GetTriangle(sideArr));
        }
    }
}

  6. 生成UnitTestDemoTests测试项目,在生成项目后,测试项将出如今测试资源管理器中。 若是测试资源管理器窗口不可见,请选择顶级 Visual Studio 菜单上的“测试”,而后依次选择“窗口(Windows)”、“测试资源管理器(Ctrl + E,T9)”,如图所示。

 

  

  7. 在测试资源管理器上能够看到刚刚所写的测试方法,这样在GetTriangle_Test单击右键选择“运行所选定的测试”就能够在Test Explorer里看到单元测试的运行结果,以下图所示。  

  

能够看到,咱们在单元测试中提供的例子的指望是输出“等边三角形”,运行结果倒是“等腰三角形”。再看一看 GetTriangle() 函数的代码,原来是在对在判断三边数值是等边三角形以后没有使用 else if 又用 if 判断为等腰三角形了。经过这个简单的单元测试就可以发现一些意向不到的错误。不要觉得这里的bug很低级,相似的状况确实会在现实中发生。

 

  8. 把上面的错误更正后,再次运行TestMethod1()就会获得测试已经过的结果,如图所示。

  

 

建立单元测试项目和测试方法,除了以上经过手动建立单元测试项目和根据你的要求进行编写测试用例以外,还能够从你的项目的方法上直接生成单元测试项目和单元测试存根,那样操做更加方便,速度也会更快一些。

2) 经过代码直接生成单元测试项目和单元测试存根
  1. 在代码编辑器窗口中,从上下文菜单右键单击并选择“建立单元测试”。

  

  1. 在建立单元测试窗口,选择默认值,或更改用于建立并命名单元测试项目和单元测试的参数值。 单击“肯定”,建立单元测试项目。

  

这里涉及测试框架的选择,MSTest是VS自带的测试框架。新的MS TEST如今是经过Nuget的包发布了,目前MS发布了两个版本:

  • MS TEST V1:V1的版本依赖于一个包: MSTest.TestFramework
  • MS TEST V2:V2的版本依赖于两个包: MSTest.TestFrameworkMSTest.TestAdapter
    这两个版本使用起来仍是大同小异的,MSTest v2 主要是为了.net core准备的,固然也能够在.net framework上运行,而且在v1上新加入了一些扩展。
  1. 在生成的测试项目中,将测试代码添加到对应单元测试方法中,以使单元测试有意义。
  2. 此后生成测试项目,并在测试资源管理器中运行测试方法,获得测试结果(与上方步骤一致)。

编写测试代码

你使用的单元测试框架和 Visual Studio IntelliSense 将指导你完成为代码项目的单元测试编写代码。 若要在测试资源管理器中运行,大多数框架要求你添加特定的属性来识别单元测试方法。 框架还提供了一种方法,一般经过断言语句或方法属性,来指示测试方法是否已经过或失败。 其余属性标识可选的安装方法,即在类初始化时和每一个测试方法和每一个拆卸方法以前的安装方法,这些拆卸方法在每一个测试方法以后和类被销毁以前运行。

AAA(准备、执行、断言)模式是编写待测试方法的单元测试的经常使用方法:

  • 准备(Arrange),单元测试方法的准备部分初始化对象并设置传递给待测试方法的数据;
  • 执行(Act),执行部分调用具备准备参数的待测试方法;
  • 断言(Assert),断言部分验证待测试方法的执行行为与预期相同。
如示例中验证 UnitTest1.GetTriangle() 函数,咱们编写了一个测试来验证方法的标准行为:
[TestMethod()]
public void GetTriangle_Test()
{
    // arrange  
    string[] sideArr = { "5", "5", "5" }; // 准备传给待测试方法的数据
    string expected = "等边三角形";

    // act  
    var actual = UnitTestClass.GetTriangle(sideArr); // 调用测试方法

    // assert  
    Assert.AreEqual(expected, actual); // 验证待测试方法的执行结果是否与预期相同
}
为单元测试设置超时值:

在某些状况下(例如经过网络获取数据),经常不但愿程序卡住而占用太多时间,经过设置测试方法的超时时间,来测试一个方法是否在预期时间内执行。

[TestMethod()]
[Timeout(2000)] // 毫秒 要在单个测试方法上设置超时时间
public void GetTriangle_Test()
{   ... 
}
[TestMethod()] [Timeout(TestTimeout.Infinite)]
// 毫秒 将超时时间设置为容许的最大值 public void GetTriangle_Test() { ... }
MSTest参数化测试:

什么是参数化测试?

答:简单的说,就是一样的逻辑,根据输入参数不一样给出不一样的结果。由于只是参数不一样,因此并不但愿把测试方法写多遍,可是又但愿对每一个参数的测试成为一个独立的测试用例。举例说,假定我有一个数学计算的方法是把两个整数相加求和,我但愿证实这个方法对于任意两个数都是经过的。

在MSTest中能够经过DataRow Attribute 来指定测试用例的参数,实现参数化测试:

/// <summary>
/// 相加(待测试方法)
/// </summary>
/// <param name="num1">数值1</param>
/// <param name="num2">数值2</param>
/// <returns>计算结果</returns>
public static int Add(int num1, int num2)
{
    return Math.Abs(num1 + num2);
}
/// <summary>
/// 测试方法
/// </summary>
[TestMethod()]
[DataRow(10, 20)]
[DataRow(-2, -5)]
[DataRow(1, -2)]
[DataRow(5, null)]
public void Add_Test(int num1, int num2)
{
    Assert.AreEqual(UnitTestClass.Add(num1, num2), num1 + num2);
}

测试了全部可能的状况,以达到更好的覆盖率。上方给出示例Add方法的单元测试运行测试结果以下图所示。

  

测试结果:测试结果指出对两个数相加操做的方法,目标方法还取了绝对值,与相应结果不符。

 

测试调试

可使用测试资源管理器为你的测试启动调试会话。 使用 Visual Studio 调试程序能够无缝地逐句得使你在单元测试和所测试项目之间来回反复。 若要开始调试:

  1. 在 Visual Studio 编辑器中,在想要调试的一个或多个测试方法中设置断点

  2. 在测试资源管理器中,选择测试方法,而后点击右键从快捷菜单选择“调试选定的测试”。

    

  3. 进入调试模式

F5 继续。

F10 执行下一行代码,但不执行任何函数调用。

F11 在执行进入函数调用后,逐条语句执行代码。

Shift + F11 执行当前执行点所处函数的剩余行。

Shift + F5 中止运行程序中的当前应用程序。可用于“中断”模式和“运行”模式。

NUnit 测试框架

 1)下载安装NUnit插件

  咱们在VS中选择工具菜单栏下的扩展和更新,选择联机并在搜索框中输入NUnit。有2个版本的Nunit适配器,分别为NUnit 3.x(最新版为3.4.1)和NUnit 2.x(最新版为2.6.4),都支持Visual Studio 2012+。若想在VS2010中集成,须要安装NUnit 2.6.4安装包(可在官网下载)与VS2010 NUnit整合插件下载,下载安装完毕就能在 VS2010 的视图=>其余窗口中看到 Visual Nunit (或使用快捷键Ctrl + F7),打开该视图,将之拖到合适的位置。

 2)建立NUnit单元测试项目

 未完待续..

 

使用 Visual Studio 2017进行实时单元测试

Live Unit Testing 是 Visual Studio 2017 版本 15.3 中提供的一项技术,可在咱们更改代码,而后保存的时候,它会自动生成自动测试,最后得出结果。

1)实时单元测试:

  • 让你更有信心地对代码进行重构和更改。 Live Unit Testing 在编辑代码时自动执行全部受影响的测试,确保所作更改不会中断测试。

  • 指示单元测试是否充分覆盖代码,并显示未被单元测试覆盖的代码。 Live Unit Testing 以图形方式实时描绘代码覆盖率,以便一眼就能看到每行代码覆盖的测试数,目和未被任何单元测试覆盖的行。

2)进行实时单元测试:

  1. 在类库项目中建立一个待测试方法,以下:
/// <summary>
/// 相加(待测试方法)
/// </summary>
/// <param name="num1"></param>
/// <param name="num2"></param>
/// <returns></returns>
public static int Add(int num1, int num2)
{
    return num1 + num2;
}

  2. 根据以上建立单元测试项目的过程,建立一个单元测试项目(测试框架可使用 Live Unit Testing 的 MSTest 测试框架(默认)。 还可以使用 xUnit 和 NUnit 测试框架)。在测试项目添加对被测试类库项目的引用来提供测试库访问权限。

  3. 在测试项目类中,将模板提供的样本单元测试方法代码替换为如下代码:

[TestMethod()]
[DataRow(10, 20)]
[DataRow(-2, -5)]
[DataRow(1, -2)]
[DataRow(5, null)]
public void Add_Test(int num1, int num2)
{
    Assert.AreEqual(UnitTestClass.Add(num1, num2), num1 + num2);
}

  4. 从顶级 Visual Studio 菜单中依次选择“测试” > “Live Unit Testing” > “启动” Visual Studio 启动 Live Unit Testing,使其自动运行全部测试。

   

  5.  完成运行测试后,“测试资源管理器” 显示总体结果和各个测试的结果。 此外,代码窗口以图形方式显示测试代码覆盖率和测试结果。 以下图所示,三项测试均已成功执行。 它还显示测试中已覆盖 Add() 方法中的全部代码路径,并已成功执行这些测试(用绿色复选标记“”指示)。 UnitTestClass.cs 中的其余方法有部分代码没有代码覆盖率(用蓝线“”指示)

  

  还可经过在代码窗口中选择一个特定的代码覆盖率图标来得到有关测试覆盖率和测试结果的更多详细信息。 若要查看此详细信息,请执行如下操做:

  单击行上的绿色复选标记“”, 以下图所示,Live Unit Testing 指示只有一个测试覆盖该行的代码,而且都已成功执行。

  

  Live Unit Testing 中“”标识的主要问题是代码覆盖率不完整,能够经过添加测试方法或改变测试参数,以下图,能够看到代码覆盖率已扩展到 GetTriangle() 的每一行代码。 

  在你修改源代码时,Live Unit Testing 将自动执行新增的和修改后的测试。

  

  6. 处理测试失败:

  将 Add() 作些许修改,修改成计算两数相加的绝对值,在保存后Live Unit Testing 指示 Add() 方法执行失败,以下图所示:  

  

7. 中止实时单元测试:

  

 

使用 IntelliTest 为你的代码生成单元测试

IntelliTest 浏览你的 .NET 代码,以生成测试数据和单元测试套件。 对于代码中的每一个语句,将生成执行该语句的测试输入。 为代码中的每一个条件分支执行案例分析。 例如,分析 if 语句、断言和可能引起异常的全部操做。 此分析用于为你的每一个方法生成参数化单元测试的测试数据,从而建立具备较高代码覆盖率的单元测试。

当你运行 IntelliTest 时,你可轻松看到哪些测试会失败,并可添加任何须要的代码来修复它们。 你可选择要保存到测试项目中的已生成测试,以提供回归套件。 当你更改代码时,从新运行 IntelliTest,以使生成的测试与你的代码更改同步。

IntelliTest 仅可用于 C# 且不支持 x64 配置。

IntelliTest 入门

若要生成单元测试,你的类型必须是公共类。 不然,先建立单元测试,而后再生成它们。

  1. 在 Visual Studio 中打开解决方案。 而后打开包含你要测试的方法的类文件。

  2. 在代码中右键单击一种方法并选择“建立 IntelliTest”,为方法中的代码建立生成单元测试项目。

  

  接受默认格式以生成测试,或更改项目和测试的命名方式。 你能够建立新的测试项目或将你的测试保存到现有项目。

  

  3. 建立测试项目成功以后,选择上图中“运行 IntelliTest”,为方法中的代码运行IntelliTest单元测试项目。

  IntelliTest 使用不一样的输入屡次运行你的代码。 每次运行都会在表中表示出来,显示输入测试数据以及产生的输出或异常。

  

  要为一个类中的全部公共方法生成单元测试,只需右键单击类而不是特定的方法。 而后选择“运行 IntelliTest”。 使用“浏览结果”窗口中的下拉列表,显示类中每一个方法的单元测试和输入数据。

  

  对于经过的测试,检查结果列中报告的结果是否与你对代码的预期要求匹配。 对于失败的测试,根据须要修复你的代码。 而后从新运行 IntelliTest 来验证修复。

相关文章
相关标签/搜索