Unit Test (单元测试) - .NET

创建时间:
2015-08-05 19:25
最近更新:
2018-08-08 16:32

Resource - MSDN

  1. 单元测试概述 (本文有提及 "测试类继承")
  2. 单元测试基础
  3. 使用单元测试
  4. 使用单元测试验证代码
  5. 使用代码覆盖率确定所测试的代码量
  6. 基本测试结果 - 已中止、没有执行、已通过但运行已中止、不可运行、已断开连接、超时、挂起、正在进行中、完成、没有结论、失败、已通过
  7. 针对现有代码创建并运行单元测试
  8. 如何:创建数据驱动的单元测试 - DataSourceAttribute, TestContext.DataRow
  9. 如何:编写在单个事务范围内运行的数据库单元测试 - TestInitializeAttribute, TestCleanupAttribute, TransactionScope 类, Distributed Transaction Coordinator 服务
  10. 如何:从 Microsoft Visual Studio 运行测试
  11. 如何:测试私有方法
  12. 泛型方法的单元测试
  13. ASP.NET Web 服务的单元测试
  14. 演练:创建并运行托管代码的单元测试 - Note: 本文对异常的单元测试做了演示
  15. ASP.NET MVC 应用程序中的单元测试
  16. 构建可测试 ASP.NET MVC 应用程序
  17. 测试驱动 ASP.NET MVC
  18. 使用 ASP.NET 单元测试
  19. Testability and Entity Framework 4.0
  20. 使用数据驱动的单元测试
  21. 使用单元测试验证数据库代码 - Entrance
  22. 在您的数据库项目中应用测试驱动的开发
  23. 在您的数据库项目中应用测试驱动的开发
  24. 使用 UI 自动化验证代码
  25. 如何:为测试部署文件
  26. TestResults 目录 in 测试部署概述
  27. TestResults 目录 in 如何: 为测试部署文件
  28. 用 Microsoft Fakes 隔离测试代码
  29. 浅谈面向对象技术在单元测试与持续集成上的应用
  30. 我最喜爱的功能:Visual Studio 11 中单元测试的增强功能
  31. My Favorite Features: Unit Testing Enhancements in Visual Studio 11
  32. 有关 Visual Studio ALM 测试工具的 API 参考 - ALM (Application Lifecycle Management, 开发生命周期管理)

常用类库

  1. 单元测试框架 - 本文对以下命名空间中的成员进行了分组,有助于理解:
  2. Microsoft.VisualStudio.TestTools.UnitTesting 命名空间
  3. Microsoft.VisualStudio.TestTools.UnitTesting.Web 命名空间
  1. 使用 Assert 类 - 在测试方法中,可以调用任意数量的 Assert 类方法。
  2. Microsoft.VisualStudio.TestTools.UnitTesting.Assert 类 - Verifies conditions in unit tests using true/false propositions.
  3. Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert 类 - Verifies true/false propositions associated with collections in unit tests.
  4. Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert 类 - Verifies true/false propositions associated with strings in unit tests.
  1. Inconclusive: 使用 Assert 类 文中提及 "只要测试生成的结果为 Inconclusive,就会引发 AssertInconclusiveException。通常,向仍在处理的测试添加 Assert.Inconclusive 语句可指示该测试尚未准备好,不能运行。"。
  2. AssertFailedException 类 - 每当断言失败时会引发它 (This is thrown whenever an assertion fails)
  3. ExpectedExceptionAttribute 类 - 表示测试方法的执行过程中应引发异常。 此属性用于测试是否引发了预期的异常。如果引发了预期的异常,则测试方法通过。如果引发的异常继承自预期的异常,则测试失败。 如果测试既有 ExpectedExceptionAttribute 又有 Assert 语句,则其中的任一因素都可能导致测试失败。要确定测试失败是由该属性还是此语句引起的,请双击测试结果打开测试的 "结果详细信息" 页。 使用 Assert 类 文中提及 "如果希望开发代码中的某方法引发异常,又想用测试方法来验证是否真的在该方法中引发了异常,则请用 ExpectedExceptionAttribute 特性来修饰测试方法。"。 Note: 在本站搜索 ExpectedException 有用法示例。

示例

  1. 单元测试示例:对应用商店应用中的 Visual C# 代码进行单元测试

Resource

  1. 单元测试 Mocking 类库需具备的特性
  2. Moq 的使用心得
  3. .NET Mocking Framework 对比
  4. 单元测试培训系列:(一) 单元测试概念以及必要性
  5. 单元测试培训系列:(三) 可测试性与重构
  6. Visual Studio 2010 单元测试 (目录)
  7. Visual Studio 恢复了快速单元测试生成功能
  8. Visual Studio 2012 找回 建立單元測試 選單
  9. 利用 Visual Studio 2013 进行跨浏览器、编码 UI 测试
  10. 单元测试之道 C# 版
  11. Visual Studio 2012 Unit Test 个人学习汇总 - 目录
  12. Visual Studio 2012 Unit Test 个人学习汇总 - 搜索
  13. 软件测试:单元测试的一些疑问
  14. 示例:Visual Studio 2012 单元测试
  15. 在 .NET 开发中的单元测试工具之 (1) - NUnit
  16. 在 .NET 开发中的单元测试工具之 (2) - xUnit.Net
  17. 单元测试中对代码的异常进行验证
  18. 快速编写 .NET 中的单元测试接口
  19. 在 .NET 中如何测试 private 和 protected 方法
  20. ASP.NET 数据库应用程序进行单元测试的配置
  21. 求助:一份用于学习单元测试的案例需求
  22. ASP.NET MVC 单元测试最佳实践 - 2009-02-23

Visual Studio 中的单元测试功能

Visual Studio 2013 - 菜单 - 测试 - 窗口 - 测试资源管理器

单元测试项目文件分析

From: http://msdn.microsoft.com/zh-CN/library/ms182517 - 单元测试分析

单元测试文件的组成部分

  • using Microsoft.VisualStudio.TestTools.UnitTesting
  • TestClassAttribute
  • TestMethodAttribute
  • TestContext
  • Assert.Inconclusive()

Additional Test Attribute (附加测试特性) (用于初始化和清理)

InitializeAttribute

CleanupAttribute

解决方案项

  • Local.testsettings:这些设置控制如何运行不收集诊断数据的本地测试。
  • Bank.vsmdi:此文件包含有关解决方案中存在的测试列表的信息,并填充“测试列表编辑器”窗口。
  • TraceAndTestImpact.testsettings:这些设置控制如何运行收集特定诊断数据集的本地测试。
  • ...

Requirements

2015-11-03 Tony 测试发现: "单元测试类" 与 "单元测试方法" 的 "可访问性级别" 均必须为 public,否则该 "单元测试方法" 不会出现在 "测试资源管理器",且 "运行测试 Ctrl+R, T"、"调试测试 Ctrl+R, Ctrl+T"、"全部运行" 均不执行该 "单元测试方法。

Test class requirements

The minimum requirements for a test class are the following:

  • The [TestClass] attribute is required in the Microsoft unit testing framework for managed code for any class that contains unit test methods that you want to run in Test Explorer.
  • Each test method that you want Test Explorer to run must have the [TestMethod]attribute.

You can have other classes in a unit test project that do not have the [TestClass] attribute, and you can have other methods in test classes that do not have the [TestMethod] attribute. You can use these other classes and methods in your test methods.

Test method requirements

A test method must meet the following requirements:

  • The method must be decorated with the [TestMethod] attribute.
  • The method must return void.
  • The method cannot have parameters.

Most Simplified Version

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace FC.CS.LibraryManagement.Bll.Test.LibraryManagement
{
    [TestClass]
    public class XxxTest
    {
        [TestMethod]
        public void Yyy()
        {
            bool isOk = true;
            Assert.IsTrue(isOk);
            Assert.IsFalse(isOk);
        }
    }
}

Tony's 单元测试命名约定

命名空间    被测对象的命名空间  + .Test
类          被测对象的类名      + _Test
方法        被测方法名          + _中文描述

Shortcut of Unit Test

测试资源管理器:Alt+S+W+T
运行测试:Ctrl+R, T
调试测试:Ctrl+R, Ctrl+T
运行所有测试:Ctrl+R, A
调试所有测试:Ctrl+R, Ctrl+A
重复上次运行:Ctrl+R, L

Unit Test Generator

Unit Test Generator - Visual Studio 2013 自动生成测试项目
Generates unit test code for methods in classes under test.
Visual Studio 2013 - Menu - Tool - 扩展和更新 - Search "Unit Test Generator".

Visual Studio 恢复了快速单元测试生成功能
通过 Unit Test Generator 快速为项目加入单元测试程序代码

某网友推荐的 .NET 单元测试工具组合

xUnit + NSubstitute + Shouldly

解决 "运行测试时无法读取 ASP.NET 项目的 Web.config 文件中的配置项"

2017-04-27 Tony 按以下步骤成功解决此问题: Test Project - Context Menu - Property - 生成事件 - 后期生成事件命令行 - 填写 copy "$(SolutionDir)Net451MvcNoAuth\Web.config" "$(ProjectDir)$(OutDir)$(TargetFileName).config" - 保存此设置。

原理: 上述设置导致 每次运行测试项目时 会将 "被测项目的 Web.config 文件" 自动复制为 "测试项目的 bin\Debug\Net451MvcNoAuth.Tests.dll.config 文件"。

Resource

  1. ASP.NET 单元测试 - 配置文件自动复制到 Visual Studio 的测试项目的方法 (另一源)
  2. Can a unit test project load the target application's app.config file?

代码片段 - 构造测试数据

[TestMethod]
public void ToBin_两种实现的对比测试()
{
    ...
    var dataUInt32 = new[] {
        new { BinStr = "11111111111111111111111111111111", Int = uint.MaxValue },
        new { BinStr = "00000000000000000000000000000000", Int = uint.MinValue },
        new { BinStr = "00000000000000000000000000000000", Int = 0U },
        new { BinStr = "00000000000000000000000000000001", Int = 1U },
        new { BinStr = "00000000000000000000000001011011", Int = 91U },
        new { BinStr = "00000000000000000001011011101111", Int = 5871U },
        new { BinStr = "00000010110111011110111110111111", Int = 48099263U }
    };
    foreach(var datum in dataUInt32) {
        Assert.IsTrue(datum.BinStr == Converter.ToBin(datum.Int));
        Assert.IsTrue(datum.BinStr == NSystem.ToBin(datum.Int));
    }
    ...
}
[TestMethod]
public void DenaryToNSystem_Bytes()
{
    var data = new[] {
        new {
            Encoding = Encoding.Unicode,
            Bytes = new byte[] { 75, 109, 213, 139 },
            NSystem = "5M6SAD7T"
        },
        new {
            Encoding = Encoding.UTF32,
            Bytes = new byte[] { 75, 109, 0, 0, 213, 139, 0, 0 },
            NSystem = "5M6S3333AD7T3333"
        },
        new {
            Encoding = Encoding.UTF8,
            Bytes = new byte[] { 230, 181, 139, 232, 175, 149 },
            NSystem = "AX9A7TB39487"
        },
        new {
            Encoding = Encoding.UTF7,
            Bytes = new byte[] { 43, 98, 85, 117, 76, 49, 81, 45 },
            NSystem = "4H6E5X745N4Q5T4K"
        },
        new {
            Encoding = Encoding.ASCII,
            Bytes = new byte[] { 63, 63 },
            NSystem = "5858"
        },
        new {
            Encoding = Encoding.BigEndianUnicode,
            Bytes = new byte[] { 109, 75, 139, 213 },
            NSystem = "6S5M7TAD"
        }
    };
    string charSet = StringPlus._39AY;
    foreach(var datum in data) {
        byte[] bytes = "测试".TlExEncode(datum.Encoding);
        Assert.IsTrue(bytes.TlExEquals<byte>(datum.Bytes));
        string nSystem = NSystem.DenaryToNSystem(charSet, bytes);
        Assert.IsTrue(nSystem == datum.NSystem);
    }
}

代码片段 - 异常的单元测试

如果仅需测试 异常的类型

[TestMethod]
[ExpectedException(typeof(CodeException))]
public void MaxValue_ValueType_异常()
{
    char c = (char)0;
    NSystem.MaxValue(c);
}

如果需要测试 异常的类型和其它细节

[TestMethod]
public void DenaryToNSystem_ValueType_异常()
{
    try {
        NSystem.DenaryToNSystem_OnlyForDemonstration(StringPlus._39AY, -1);
    }
    catch(Exception ex) {
        Assert.IsTrue(ex.GetType() == typeof(OverflowException));
        //当上一行通过测试时,以下两行均能通过测试,因为 整个基类链 is 均为 true。因此应使用上一行、而非下两行。
        //Assert.IsTrue(ex is Exception);
        //Assert.IsTrue(ex is OverflowException);
        Assert.IsTrue(ex.Message == "值对于 UInt64 太大或太小。");
    }
}