Visual Studio创建测试工程入门

单元测试是每个程序员都应该知道并且实际应用的知识,特别对于某些特定情况下,如接口开发和代码重构时,单元测试显得尤为重要。笔者之前的代码测试都依赖于手工,近期才使用框架或自己写的测试小程序,虽然很早就了解过NUnit等框架,但是却一直没用,最近抽出一点时间来学习下代码的单元测试知识。

本文主要介绍在微软Unit Test框架对托管代码(VB.Net和C#)的测试流程,内容比较简单,主要是介绍下整个测试的流程。参考自 msdn unit test章节。

本文目录

  • 前期准备
  • 创建单元测试工程、添加测试类
  • 添加测试方法
  • 编译与运行测试
  • 使用单元测试提升代码质量

这里使用的微软自带的Unit test框架,其Test Explorer除了可以运行MS Unit框架的测试代码之外,还实现了可以支持第三方测试框架的适配器,如NUnit,后续文章将会介绍如何使用NUnit框架。

前期准备

首先新建一个控制台或类库工程,将下面代码拷贝:

using System;   

namespace BankAccountNS  
{  
    /// <summary>   
    /// Bank Account demo class.   
    /// </summary>   
    public class BankAccount  
    {  
        private string m_customerName;  

        private double m_balance;  

        private bool m_frozen = false;  

        private BankAccount()   {            }  

        public BankAccount(string customerName, double balance)  
        {  
            m_customerName = customerName;  
            m_balance = balance;  
        }  

        public string CustomerName  
        {  
            get { return m_customerName; }  
        }  

        public double Balance  
        {  
            get { return m_balance; }  
        }  

        public void Debit(double amount)  
        {  
            if (m_frozen)  
            {  
                throw new Exception("Account frozen");  
            }  

            if (amount > m_balance)  
            {  
                throw new ArgumentOutOfRangeException("amount");  
            }  

            if (amount < 0)  
            {  
                throw new ArgumentOutOfRangeException("amount");  
            }  

            m_balance += amount; // intentionally incorrect code  
        }  

        public void Credit(double amount)  
        {  
            if (m_frozen)  
            {  
                throw new Exception("Account frozen");  
            }  

            if (amount < 0)  
            {  
                throw new ArgumentOutOfRangeException("amount");  
            }  

            m_balance += amount;  
        }  

        private void FreezeAccount()  
        {  
            m_frozen = true;  
        }  

        private void UnfreezeAccount()  
        {  
            m_frozen = false;  
        }  

        public static void Main()  
        {  
            BankAccount ba = new BankAccount("Mr. Bryan Walton", 11.99);   

            ba.Credit(5.77);  
            ba.Debit(11.22);  
            Console.WriteLine("Current balance is ${0}", ba.Balance);  
        }  

    }  
}  

创建单元测试工程

在工程中,新添加一个测试工程: Visual C#->Test->Unit Test Project, 然后将class1.cs重命名为BankAccountTest.cs,为测试工程添加前面新建的控制台工程BankAccountNS引用,并引入BankAccountNS命名空间。至此,该文件看起来就像下面这样:

// unit test code  
using System;  
using Microsoft.VisualStudio.TestTools.UnitTesting;  

using BankAccountNS;

namespace BankTests  
{  
    [TestClass]  
    public class BankAccountTests  
    {  
        [TestMethod]  
        public void TestMethod1()  
        {  
        }  
    }  
}  

从上面我们可以看到,测试类需要有明确的[TestMethod]属性标志,测试方法则需要[TestMethod]标志。实际上,在测试工程中,可以包含无TestMethod属性的类,当然测试类中也可以包含无TestMethod的方法,这些非测试类、方法都可以用来供测试类、测试方法调用。

添加测试方法

下面添加一个简单的测试方法,来验证取款功能。代码如下:

    [TestMethod]
    public void Debit_WithValidAmount_UpdatesBalance()
    {
        double beginningBalance = 11.99;
        double debitAmount = 4.55;
        double expected = 7.44;

        BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);

        //Act
        account.Debit(debitAmount);

        //assert
        double actual = account.Balance;
        Assert.AreEqual(expected, actual, 0.0001, "Account not debited correctly.");
    }

方法很简单,最后通过使用Assert宏来判断代码的实际结果和期望结果是否一致,如果一致则测试通过,若不一致,测试运行时会提示失败及错误信息。

特殊的测试方法

有时候我们在测试某一个功能点时需要做一些预先初始化或测试完后的清理工作,而这些操作对于每一个测试用例都是通用的,因此如果将其放在具体的测试方法内的话会显得重复。因此,VS给我们提供了一个解决方案:

    [TestInitialize]
    public void Init()
    {
        //在每一个TestMethod执行前运行
    }

    [TestCleanup]
    public void CleanUp()
    {
        //在每一个TestMethod执行后运行
        // Assert.Fail("clean up failed.");
    }

只需给方法添加上TestInitialize、TestCleanup的属性标签,就能对测试流程进行初始化和清理工作了。

需要注意的是,这组方法是在每一个TestMethod执行前后都起作用的,也就是说如果你运行两个TestMethod1,TestMethod2,那么实际上的执行顺序是,
TestInitialize->TestMethod1->TestCleanup, TestInitialize->TestMethod2->TestCleanup.

编译与运行测试

运行测试功能很简单,先编译通过之后。

在TEST->Windows->Test Explorer中打开测试导航栏,这是可以看到我们之前写的方法,然后点击Run All或者Run某一个测试方法即可。 如下图是运行之后的例子,

test_result.png

由上图可见,绿色打勾显示的方法表示运行成功,而红色打叉的方法则是运行失败。点击具体方法后会在下面显示异常的具体错误信息。

使用单元测试提升代码质量

上一节的图是我自己写的几个简单的测试例子,无需对具体的测试方法进行说明,有关更多的信息可以参考文末的MSDN文档。这里主要说一下,单元测试应该关注的点:

  • 要验证方法的功能完整性,即期望完成的功能
  • 要注意边界值和不合法输入问题处理,程序的Bug往往来自这两方面,应该重点关注
  • 要注意case分支的完整性,即方法从入口到返回的所有分支路径都要覆盖才能算完整

Reference

MSDN-1

标签: VS Test, Visual Studio, UnitTest, 单元测试

添加新评论