惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

IT之家
IT之家
NISL@THU
NISL@THU
The Hacker News
The Hacker News
C
CXSECURITY Database RSS Feed - CXSecurity.com
T
Tenable Blog
Forbes - Security
Forbes - Security
V2EX - 技术
V2EX - 技术
Webroot Blog
Webroot Blog
Schneier on Security
Schneier on Security
T
The Exploit Database - CXSecurity.com
T
Tor Project blog
C
Cisco Blogs
TaoSecurity Blog
TaoSecurity Blog
The Last Watchdog
The Last Watchdog
PCI Perspectives
PCI Perspectives
O
OpenAI News
C
Cyber Attacks, Cyber Crime and Cyber Security
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Google Online Security Blog
Google Online Security Blog
宝玉的分享
宝玉的分享
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
量子位
D
Docker
AI
AI
Blog — PlanetScale
Blog — PlanetScale
S
Security @ Cisco Blogs
S
Schneier on Security
The GitHub Blog
The GitHub Blog
W
WeLiveSecurity
云风的 BLOG
云风的 BLOG
M
MIT News - Artificial intelligence
P
Privacy International News Feed
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
H
Hackread – Cybersecurity News, Data Breaches, AI and More
B
Blog
C
Check Point Blog
A
About on SuperTechFans
D
Darknet – Hacking Tools, Hacker News & Cyber Security
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Engineering at Meta
Engineering at Meta
I
InfoQ
T
Threat Research - Cisco Blogs
Project Zero
Project Zero
Cloudbric
Cloudbric
MongoDB | Blog
MongoDB | Blog
Cisco Talos Blog
Cisco Talos Blog
L
Lohrmann on Cybersecurity
S
Securelist

博客园 - 風語者·疾風

转载:ZooKeeper Programmer's Guide(中文翻译) Python【map、reduce、filter】内置函数使用说明(转载) Python使用基础 Redis Sentinel:集群Failover解决方案(转载) Sentinel-Redis高可用方案(二):主从切换 Sentinel-Redis高可用方案(一):主从复制 照片方案 8天学通MongoDB(实际操作版)——第九天 构建学习型部署环境 8天学通MongoDB(实际操作版)——第一天 基础入门 构建Ubuntu Server试验环境 My Record Series: (1.3) Windows Phone 7 Application Events My Record Series: (1.2) Windows Phone 7 Launchers and Choosers My Record Series: (1.1) Windows Phone 7.1 Development Environment 结合测试驱动TDD实施单元测试UnitTest 架构评审表(Architecture & Design Review Check List) 单元测试培训系列:(三)可测试性(Testability)与重构Refactoring 单元测试培训系列:(一)单元测试概念以及必要性 .NET Mocking Framework对比 Ajax-Enabled WCF Service中如何获取到Response
单元测试培训系列:(二)单元测试与Visual Studio
風語者·疾風 · 2011-07-11 · via 博客园 - 風語者·疾風

单元测试需要有一定的工具和框架的支撑,在早期,一般我们使用的都是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);
}
}
}