3. 抛出异常或捕捉异常,任何函数如果存在内部断点或跳出点,一定要做单元测试模拟程序内部跳转和停止的情况;

  4. 越底层函数越需要单元测试,指的是系统越底层的函数、越核心的函数,越需要单元测试,反之,越高层的,比如UI层,越不要单元测试。

  记住,不要去测试类中的每个方法。挑选以上几种函数进行测试,而且要根据需求来测试这个类对外所能提供的功能, 这些功能可能是其中的几个重要方法, 可能需要类中的几个方法协作,可以对这几个方法进行测试,无需测试类中的所有方法。将来做自动化回归测试的时候,这些重要函数的单元测试都有覆盖跑到,能大大增强软件的质量保证。(额外好处:你可以通过你的测试代码告诉别人如何使用这个类)

  到底是先写测试函数,后写函数,还是反之,这个问题TDD主张先写测试函数确定接口,然后写具体函数实现。个人觉得看项目需要。

  有关回归测试:

  写单元测试的一大好处是可以自动进行回归测试,也是说,将来代码修改了,可以自动跑一些所有测试案例,知道这次修改有没有伤及其他函数。回归测试的功能非常重要,对软件后期的维护和升级节省相当的成本。甚至有人说,如果没有回归测试,没必要写单元测试。

  当然,单元测试另一个问题是覆盖率,不完全的测试覆盖率意味着缺陷的存在,但不能因为覆盖率不能覆盖不做单元测试,所谓“高效的单元测试”是取得项目测试时间、项目的输出和质量、Schedule和成本的三者的平衡。

  分层架构下的单元测试:

  Web层,界面层,或称为表现层(Presentation):主要测试Controller的数据结构化逻辑。

  业务逻辑层(Business Logic):主要测试业务规则,独立的业务规则或者叠加的业务规则;以来外部对象的可以用MOCK来模拟环境测试。当然繁杂的业务流程可以不测试,要视具体情况而定。

  数据访问层(Data Access Logic): 需要测试。

  面向方面(AOP)和其他基类库:需要重点测试。

  单元测试的自动化工具:

  CppUnit,这是C++单元测试工具的鼻祖,免费的开源的单元测试框架.

  C++Test,这是Parasoft公司的产品。[C++Test是一个功能强大的自动化C/C++单元级测试工具,可以自动测试任何C/C++函数、类,自动生成测试用例、测试驱动函数或桩函数,在自动化的环境下极其容易快速的将单元级的测试覆盖率达到]。Visual Unit,简称VU,这是国产的单元测试工具,据说申请了多项专利,拥有一批创新的技术,不过老纳只关心是不是有用和好用。

  NUnit,这是一个单元测试框架,专门针对于.NET来写的

  JUnit,针对Java的单元测试框架。目前java下的team 开发采用cvs(版本控制) + ant(项目管理) + junit(集成测试) 的模式时,通过对ant的配置,可以很简单地实现测试自动化。

  单元测试的例子:

  1. NUnit单元测试,测试单个函数的逻辑分支和抛出的异常:

  实现类:(ConnectionFactory.cs)主要实现了CreateConnection这个方法。

using System;
using Microsoft.Practices.SmartClient.ConnectionMonitor.Implementations;
namespace Microsoft.Practices.SmartClient.ConnectionMonitor
{
    /// <summary>
    /// A simple factory that can create <see cref="Connection"/> objects.
    /// </summary>
    public class ConnectionFactory
    {
        /// <summary>
        /// DesktopConnection
        /// </summary>
        public const string DesktopConnection = "DesktopConnection";

        /// <summary>
        /// NicConnection
        /// </summary>
        public const string NicConnection = "NicConnection";

        /// <summary>
        /// WirelessConnection
        /// </summary>
        public const string WirelessConnection = "WirelessConnection";

        /// <summary>
        /// WiredConnection
        /// </summary>
        public const string WiredConnection = "WiredConnection";

        /// <summary>
        /// Creates a <see cref="Connection"/> object.
        /// </summary>
        /// <param name="connectionType">The type of the connection to create.</param>
        /// <param name="price">The price of the <see cref="Connection"/>.</param>
        /// <returns>A <see cref="Connection"/> object.</returns>
        /// <exception cref="ConnectionMonitorException">Thrown when an invalid type is requested.</exception>
        /// <remarks>
        /// For the built-in <see cref="Connection"/> types
        /// (i.e. DesktopConnection, NicConnection, WirelessConnection, WiredConnection),
        /// only the class name is required.  For user created types, the fully qualified
        /// class name is required.
        /// </remarks>
        public static Connection CreateConnection(string connectionType, int price)
        {
            Guard.StringNotNullOrEmpty(connectionType, "connectionType");
           
            if (price < 0)
            {
                throw new ArgumentOutOfRangeException("price");
            }

            Connection connection;
            switch (connectionType)
            {
                case DesktopConnection:
                    connection = new DesktopConnection(DesktopConnection, price);
                    break;
                case NicConnection:
                    connection = new NicConnection(NicConnection, price);
                    break;
                case WirelessConnection:
                    connection = new WirelessConnection(WirelessConnection, price);
                    break;
                case WiredConnection:
                    connection = new WiredConnection(WiredConnection, price);
                    break;
                default:
                    try
                    {
                        Type connectionTypeToCreate = Type.GetType(connectionType, true);
                        Object createdObject = Activator.CreateInstance(connectionTypeToCreate, connectionTypeToCreate.Name, price);
                        connection = createdObject as Connection;
                    }
                    catch(TypeLoadException ex)
                    {
                        throw new ConnectionMonitorException("Unsupported connection type.", ex);
                    }
                   
                    if (connection == null)
                    {
                        throw new ConnectionMonitorException("Unsupported connection type.");
                    }
                    break;
            }
            return connection;
        }

    }
}