zoukankan      html  css  js  c++  java
  • 单元测试分享

    首先介绍一下什么是单元测试:

    那么,如何检测代码的功能逻辑是否正确呢?
    调试,是临时的,且不完整的,例如,一个函数有十多种输入,调试不可能全部覆盖,能五六种就不错了。而系统测试,并不针对某个具体的函数,不关注某个函数的功能逻辑是否正确。
    要检测某个函数的功能逻辑,就必须要依照分类列出数据,检测代码是否对每一个分类都做了处理,而且每一个分类的处理是否正确。
    ——这就是单元测试。

    单元测试的基本方法 由上面的分析可以看出,单元测试的基本方法就是:依数据的分类列出输入,执行被测试程序,然后,判断输出是否符合预期。

    单元测试能达到什么样的效果呢?那就是:无论别人怎么样,我总是对的!
    这里的“别人”,是指关联代码。“我”,是指当前正在编写或测试的代码。单元测试要做到的是,无论关联代码是否有错,都要保证我是对的。具体来说,我要考虑关联代码会产生什么样的数据,这些数据要如何分类处理,只要我的分类和处理是正确的,那么,无论别人怎么样,我总是对的。

     单元测试的优点:

    1.   验证行为------验证程序的正确性
    2.   设计行为
    3.   文档行为------通过看单元测试,能够知道函数实现的什么功能,传入的参数情况,以及异常情况的处

    单元测试的技巧:

    单元测试主要关注点就是程序出错的地方: 空、边界 和异常

    当对象为空会怎样? 设计容器操作时,算法是否会超过边界? 出错了是否抛出了指定异常。

    单元测试编写方法:(配置式编程)

     编写单元测试时,最好能够批量进行测试,将测试数据与测试代码分离。当心增加测试用例时,只要新增加测试数据集即可。

    单元测试的原则:

    1.测试代码和被测代码是同等重要的,需要被同时维护

         测试代码不是附属品

        不但要重构代码,也要重构单元测试

    2. 单元测试一定是隔离的

         一个测试用例的运行结果不能影响其他测试用例

        测试用例不能相互依赖,应该能够以任何次序执行

    3. 单元测试一定是可以重复执行的

       不能依赖环境的变化(依赖时间,依赖环境变量)

       保持单元测试的简单性和可读性(维护成本高)

    4. 尽量对接口进行测试

    5. 单元测试应该可以迅速执行

         使用Mock 对象对数据库,网络的依赖性进行解耦

    6. 自动化单元测试

         持续集成

     单元测试位置:

     1. 和被测代码放到一起

           方便查找,但是会造成扰乱

    2. 放到单独的目录下,但是保证和被测代码位使用同样的包名

         方便查找

         可以直接访问包级的变量和方法

    3. 最好创建一个单元测试的project,让它依赖被测试的project

     测试用例的组织和命名:

    1. 对每个project 创建一个单元测试的project
    2. 对每个单元测试project

               创建一个AllTest.cs(根据语言不同后缀不同) ,它包含了所有的package级别的测试用例

            3.  对每个 package

                 创建一个《packageName》AllTest.cs,它包含了同一个package下所有的测试用例

            4.  对每个要测试的类

                  提供一个或者多个xxTest的测试用例

    单元测试框架

    在写单元测试之前首先需要选一个单元测试框架,不同的语言都有对应的单元测试框架,比如Java--》junit, c# ---》xunit, python--》pyUnit 等等。

    当然我可以用python 做Java语言写的程序的单元测试,我们一般都是用对应语言做对应程序的单元测试。

    下面我举的例子都以xunit为例子。

    我们执行程序时,都有个入口函数,比如 main函数,在单元测试中也有,xunit 中有[Fact]和[Theory] 两种。 对结果的预期 都是通过断言:Assert

     下面通过一个简单的例子来说一下怎样写单元测试:

    public  int fibona(int n){

      if (n==1 || n==2){

        return 1;

    }

     int first =1;

     int  second =1;

     for(int i=1;i<n;i++) {

         var temp = second;

            second += first;

            first = temp;

    }

      return second;

    }

     [Fact]

    public void Test_Fibonacci()

    {

        var act = Fibonacci(10);

        var expect = 55;

        Assert.True(act == expect);

    }

    [Theory]

    [InlineData(10,55)]

    public void Test_Fibonacci_N(int n, int expect)

    {

        var act = Fibonacci(n);   

        Assert.True(act == expect);

    }

    第二种的好处就是,我们可以传入一组数据,不需要修改代码。

    异步方法的支持:

    xunit 支持async 和 await 测试

    异常的判断:

    Assert.Throws    验证测试代码抛出指定异常

    Assert.ThrowsAsync()   如果测试代码返回Task,应该使用异步方法

    Mock 的使用:

    什么是Mock? Mock 就是假的,模拟的,我们什么时候会用到这个呢? 下面列出情景:

     真实的对象不易构造

        例如httpselvlet必须在servlet容器中才能创建处理

     真实的对象非常复杂

       如jdbc中的Connection,ResultSet

    真实的对象行为具有不确定性,难于控制他们的输出或者返回结果

    真实的对象有些难于触发

        例如:硬盘已满,网络连接断开

    真实的对象可能还不存在,例如依赖的另外一个模块还没有开放完毕

    下面列举一个例子:

    首先定义一个接口ICalculator:

    using System;

    using System.Collections.Generic;

    using System.Linq;

    using System.Text;

     

    namespace CalculatorPkg

    {

        public interface ICalculator

        {

            int Add(int param1, int param2);

            int Subtract(int param1, int param2);

            int Multipy(int param1, int param2);

            int Divide(int param1, int param2);

            int ConvertUSDtoRMB(int unit);

        }

    }

    再定义一个类实现这个接口ICalculator :

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;


    namespace CalculatorPkg
    {
    public class Calculator : ICalculator
    {
    private readonly IUSD_RMB_ExchangeRateFeed _feed;
    public Calculator(IUSD_RMB_ExchangeRateFeed feed)
    {
    this._feed = feed;
    }
    #region ICalculator Members
    public int Add(int param1, int param2)
    {
    throw new NotImplementedException();
    }
    public int Subtract(int param1, int param2)
    {
    throw new NotImplementedException();
    }
    public int Multipy(int param1, int param2)
    {
    throw new NotImplementedException();
    }
    public int Divide(int param1, int param2)
    {
    return param1 / param2;
    }
    public int ConvertUSDtoRMB(int unit)
    {
    return unit * this._feed.GetActualUSDValue();
    }
    #endregion
    }
    }

    初始化类对象时,需要传入接口类对象:

    namespace CalculatorPkg

    {

         public interface IUSD_RMB_ExchangeRateFeed

        {

            int GetActualUSDValue();

        }

    }

    在对类 Calculator  写单元测试时,首先需要初始化,但是我们不知道 接口类对象具体返回什么,所以我们采用Mock方式:

    using System;

    using Xunit;

    using System;

    using System.Collections.Generic;

    using System.Linq;

    using System.Text;

     

     

     

    using CalculatorPkg;

     

    using Moq;

     

    namespace CalculatorPkgTests

    {

        public class CalculatorTester

        {

           

            // 定义mock的逻辑

            private IUSD_RMB_ExchangeRateFeed PrvGetMockExchangeRateFeed()

            {

                Mock<IUSD_RMB_ExchangeRateFeed> mockObject = new Mock<IUSD_RMB_ExchangeRateFeed>();

                mockObject.Setup(m => m.GetActualUSDValue()).Returns(500);

                return mockObject.Object;

            }

            

          [Theory]

           [InlineData(9,3,3)]

            [InlineData(4,2,2)]

            public void TC1_Divide(int a, int b, int exceptResult)

            {

                IUSD_RMB_ExchangeRateFeed feed =this.PrvGetMockExchangeRateFeed();

                Calculator calculator = new Calculator(feed);

                int actualResult = calculator.Divide(a, b);          

                Assert.Equal(expectedResult, actualResult);

            }

         

        }

    }

  • 相关阅读:
    [转]史上最最佳软件开发实践指导
    附微信表情代码关键字
    TFS2010配置篇 — 万事具备只欠外网访问
    ASP.NET在IE10中Form表单身份验证失效问题解决方法
    TFS2010 更改已经登录的用户
    TFS命令tf:undo 强制签入别人签出的文件
    ASP.NET Core部署后报错: Could not load file or assembly 'System.Private.ServiceModel, Version=4.1.2.4, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
    SQL 增删改查语句
    存储过程拼接sql
    sql 求时间差
  • 原文地址:https://www.cnblogs.com/NancyStartOnce/p/6803174.html
Copyright © 2011-2022 走看看