zoukankan      html  css  js  c++  java
  • 单元测试培训系列:(二)单元测试与Visual Studio

    单元测试需要有一定的工具和框架的支撑,在早期,一般我们使用的都是NUnit这套单元测试框架进行。后来微软在Visual Studio中集成了单元测试功能后,提供了更为强劲的功能以及集成整合能力,就没有必要再继续使用Nunit了。

    这一章节,主要就是介绍Visual Stuido中常见的单元测试相关的Attribute的功能和使用场景。

    基本类Attribute

    TestClassAttribute

    用于标识包含测试方法的类。任何一个单元测试类,必须在类上添加该Attribute,否则不会被Visual Studio识别为单元测试类,里面的所有方法也无法正确识别;

    TestMethodAttribute

    用于标记测试方法。只有添加了该Attribute的方法才会被识别为单元测试方法,才会被添加到Test List中;

    PriorityAttribute

    用于标记单元测试方法的执行优先级,即先后顺序。(但我测试时候未能生效,别的朋友有成功过,具体原因不详)

    DescriptionAttribute

    标记并描述当前单元测试方法,会出现在Test List中(默认不显示这列,需要手动添加),方便在运行测试时,方便的了解测试方法的含义

    TestPropertyAttribute

    传递Key/Value元数据的一个Attribute,本身没什么特别意义,允许在一个方法上标记多个。

    初始化与清理类Attribute

    此类别的所有属性都必须写在静态方法上

    AssemblyInitializeAttribute

    当一个程序集中第一个单元测试方法被执行前会执行该方法,用于初始化操作

    ClassInitializeAttribute

    当第一次执行当前类中的单元测试方法前会执行该方法,用于初始化操作

    TestInitializeAttribute

    当前类中的每个单元测试方法都会先执行该方法

    TestCleanupAttribute

    当前类中每个单元测试方法后,都会执行该方法,用于卸载和清除

    ClassCleanupAttribute

    当执行完该类所有单元测试之后,执行该方法

    AssemblyCleanup

    当当前程序集中所有单元测试都执行完后,执行该方法

    异常以及超时类Attribute

    ExpectedExceptionAttribute

    标记当前单元测试应该出现的异常,如果应该出现的指定类型的异常没有出现,则认为这个单元测试失败

    TimeoutAttribute

    设定当前单元测试的最大执行时间,如果该方法执行的时间超过了该值,则会认为该单元测试未能通过

    特殊作用类Attribute

    DataSourceAttribute

    逐行读取指定数据源中的数据,每行数据会执行当前单元测试方法一次。通过testContextInstance对象上的DataRow索引器属性访问每行上的各列数据。

    需要注意的是,以上绝大多数Attribute并不能使用在单元测试中,而是在集成测试中使用


    以下附上一段典型的单元测试代码作为展示

    View Code
    using System;
    using System.Data;
    using System.Reflection;
    using System.Text;
    using System.Collections.Generic;
    using System.Linq;
    using ALite.Core.UnitTestSimple;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Moq;

    namespace ALite.Core.Tests
    {
    [TestClass]
    public class OrderUnitTest
    {
    private TestContext _testContextInstance;
    public TestContext TestContext
    {
    get { return _testContextInstance; }
    set { _testContextInstance = value; }
    }

    [TestMethod]
    [Description(
    "A normal unit testing for the method 'Submit.'")]
    public void TestNormalSubmit()
    {
    // Mock
    var mockPersistence = new Mock<IPersistence>();
    mockPersistence.Setup(e
    => e.Save(It.IsAny<Order>())).Returns(true);

    // Arrange
    var target = new Order
    {
    PesistenceHandler
    = mockPersistence.Object
    };
    const bool expected = true;

    // Action
    bool result = target.Submit();

    // Assert
    Assert.AreEqual(expected, result);
    }

    [TestMethod]
    // 是且只有是抛出DataException类型的异常时才会算通过,即便是子类也会判断为未通过
    [ExpectedException(typeof(DataException))]
    [Description(
    "A unit testing for the method 'Submit' with a DataException when using the property PersistenceHandler")]
    public void TestSubmitWithException()
    {
    // Mock
    var mockPersistence = new Mock<IPersistence>();
    mockPersistence.Setup(e
    => e.Save(It.IsAny<Order>())).Throws(new DataException());

    // Arrange
    var target = new Order
    {
    PesistenceHandler
    = mockPersistence.Object
    };
    const bool expected = true;

    // Action
    bool result = target.Submit();

    // Assert
    Assert.AreEqual(expected, result);
    }

    [TestMethod]
    [Description(
    "举例说明如何测试私有方法和使用TestProperty属性")]
    [TestProperty(
    "City", "深圳")]
    [TestProperty(
    "Province", "广东")]
    [TestCategory(
    "Category1")]
    public void TestCountTotalPrice()
    {
    var method
    = Util.GetCallingMethod(false, 0);
    var attributeType
    = typeof(TestPropertyAttribute);
    var attributes
    = method.GetCustomAttributes(attributeType, true);

    var city
    = ((TestPropertyAttribute)attributes[0]).Value;
    var province
    = ((TestPropertyAttribute)attributes[1]).Value;

    var address
    = string.Format("{0} {1}", province, city);

    // Mock
    var mockDeliveryManageHandler = new Mock<IDeliveryManage>();
    mockDeliveryManageHandler.Setup(e
    => e.CountDeliveryFee(It.IsAny<string>())).Returns(10.00m);

    // Arrange
    var orderDetails1 = new OrderDetail()
    {
    TotalPrice
    = 10.00m
    };

    var orderDetails2
    = new OrderDetail()
    {
    TotalPrice
    = 20.00m
    };

    var orderDetails3
    = new OrderDetail()
    {
    TotalPrice
    = 30.00m
    };

    var details
    = new List<OrderDetail> {orderDetails1, orderDetails2, orderDetails3};

    var target
    = new Order()
    {
    Destination
    = address,
    DeliveryManageHandler
    = mockDeliveryManageHandler.Object,
    OrderDetails
    = details
    };

    const decimal expected = 70m;

    // Action
    // 通过反射测试私有方法,比IDE提供的方式会更灵活一些
    var typeTarget = typeof (Order);
    var methodTarget
    = typeTarget.GetMethod("CountTotalPrice", BindingFlags.Instance | BindingFlags.NonPublic);
    var objResult
    = methodTarget.Invoke(target, null);

    // Assert
    Assert.AreEqual(expected, (decimal)objResult);
    }

    [DeploymentItem(
    "1.1-ALite.Core.Tests\\1.xml"), TestMethod]
    [DataSource(
    "Microsoft.VisualStudio.TestTools.DataSource.XML", "|DataDirectory|\\1.xml", "Address", DataAccessMethod.Sequential)]
    [Description(
    "举例说明如何使用DataSourceAttribute属性, 该单元测试会读取数据源中的每一行数据进行测试")]
    public void TestCountTotalPriceWithVarious()
    {
    var province
    = this._testContextInstance.DataRow["Province"];
    var city
    = this._testContextInstance.DataRow["City"];
    var fee
    = Convert.ToDecimal(this._testContextInstance.DataRow["Fee"]);

    var address
    = string.Format("{0} {1}", province, city);

    // Mock
    var mockDeliveryManageHandler = new Mock<IDeliveryManage>();
    mockDeliveryManageHandler.Setup(e
    => e.CountDeliveryFee(It.IsAny<string>())).Returns(10.00m);

    // Arrange
    var orderDetails1 = new OrderDetail()
    {
    TotalPrice
    = 10.00m
    };

    var orderDetails2
    = new OrderDetail()
    {
    TotalPrice
    = 20.00m
    };

    var orderDetails3
    = new OrderDetail()
    {
    TotalPrice
    = 30.00m
    };

    var details
    = new List<OrderDetail> { orderDetails1, orderDetails2, orderDetails3 };

    var target
    = new Order()
    {
    Destination
    = address,
    DeliveryManageHandler
    = mockDeliveryManageHandler.Object,
    OrderDetails
    = details
    };

    decimal expected = fee;

    // Action
    // 通过反射测试私有方法,比IDE提供的方式会更灵活一些
    var typeTarget = typeof(Order);
    var methodTarget
    = typeTarget.GetMethod("CountTotalPrice", BindingFlags.Instance | BindingFlags.NonPublic);
    var objResult
    = methodTarget.Invoke(target, null);

    // Assert
    Assert.AreEqual(expected, (decimal)objResult);
    }
    }
    }
  • 相关阅读:
    用MobaXterm_Personal_12.0远程连接ubuntu虚拟机,并解决报错connection refused
    二叉树图解以及二叉树的递归原理
    Source Insight 4.0 安装使用简单粗暴教程
    VScode配置嵌入式linux开发环境,代替source insight
    linux系统下部署DNS正向解析
    【剑指Offer】面试题47. 礼物的最大价值
    【LeetCode】1371. 每个元音包含偶数次的最长子字符串
    【LeetCode】680. 验证回文字符串 Ⅱ
    【剑指Offer】面试题46. 把数字翻译成字符串
    【剑指Offer】面试题45. 把数组排成最小的数
  • 原文地址:https://www.cnblogs.com/Xrinehart/p/2102669.html
Copyright © 2011-2022 走看看