单元测试是通过抛出异常的方式向Visual Studio指示失败的。未抛出异常的测试都认为是通过的,除非测试针对的是ExpectedException特性,单元测试框架定义了Assert对象。在FibonacciTest()方法中添加代码:
  1         [TestMethod]
  2         public void FibonacciTest()
  3         {
  4             const int FACTOR = 8;
  5             const int Expected = 21;
  6             int actual = ExtendedMath.Functions.Fibonacci(FACTOR);
  7
  8             Assert.AreEqual(Expected, actual);
  9         }
  在编写单元测试方法时,3A模式很有帮助——Arrange, Act, Assert. 首先建立变量安排测试,然后测试调用代码,后断言调用代码是否与期望值一致。对于每个A都应该建立相对独立的代码段。
  使用TestInitialize特性创建一个方法,该方法将会在当前类的每个单元测试方法执行之前执行一次。类似的,TestCleanup总是在每个测试执行之后立即执行。与单元测试类似,包含有这些特性的方法必须是公有的和非静态的,并且没有参数和返回值。TestInitialize运行之后立即运行单元测试,单元测试运行之后立即运行TestCleanup。
  无论进行多少次单元测试ClassInitialize和ClassCleanup特性均在当前类中只运行一次。这两个方法是静态的,并接受一个TestContext实例作为参数。
  可以使用ClassInitialize和ClassCleanup控制类一级的操作,而对于程序集一级的操作则需要用到AssemblyInitialize和AssemblyCleanup特性。例如,用AssemblyInitialize修饰的方法会在当前程序集的任意一个测试执行之前执行,而不是仅仅针对当前类中的测试。这些方法也必须是静态的,且接受一个TestContext类型的参数。
  当多个类之间有公共操作时,可以考虑使用AssemblyInitialize和AssemblyCleanup特性,但此时并不是针对每个类都调用初始化和清除方法,而是使用程序集一级的方法。
  确定单元测试成功与否的常用方法是将期望结果与实际结果进行比较。
  Assert.AreSame/Asser.AreNotSame常见用法是保证属性返回期望的实例,或集合能正确处理引用,它们用来验证两个参数状态相同的情况下实际上是否指向同一个对象。
  通常,单元测试都会有一个TestContext实例的引用,该对象用于向测试提供运行时的功能,如测试本身的详细信息、所用的各种目录以及提供存储在测试结果中详细信息的方法等。
  对代码行为进行验证的好的方法是利用真实的数据执行代码。Visual Studio提供了为单元测试自动绑定数据源作为输入的功能。单元测试将会对每个数据行都运行一次。
  如何对类的非公有成员进行测试呢?假如正在编写一个私有函数,它不可以被公开访问,而只能由内部其他成员访问,我们想要测试该方法,但测试代码却无法访问私有成员,通常有4种方法处理:
  1. 将需要测试的私有成员更改为公有成员
  2. 将需要测试的私有成员更改为内部成员,并将测试程序集添加到原始程序集的internalsVisibleTo特性中
  3. 保证需要测试的私有成员能够通过公有成员访问,然后通过那些公有成员进行测试
  4. 在测试中用.NET反射加入并直接调用那些非公有成员
  假设要对下面这个类的私有数据字段和方法进行测试:
  1     public class Example
  2     {
  3         public Example()
  4         { }
  5         private string password = "letmein";
  6         private bool VerifyPassword(string password)
  7         {
  8             return (string.Compare(this.password, password, false) == 0);
  9         }
  10     }
  Visual Studio引入了PrivateObject类,它对反射代码进行了封装,通过这个类可以以一种非常直接的方式访问非公有成员。要使用这个类,首先需要创建该类的一个实例,其参数是所需处理的类的Type对象:
1 namespace Explorations
2 {
3     [TestClass]
4     public class ExampleTest
5     {
6         private PrivateObject privateObject;
7         const string PASSWORD = "letmein";
8
9         [TestInitialize]
10         public void TestInitialize()
11         {
12             privateObject = new PrivateObject(typeof(Example));
13         }
14     }
15 }