《Pro ASP.NET MVC 3 Framework》学习笔记之十【Unit Testing的使用】

简介:

前面的笔记做了关于Ninject(MVC三类工具里面第一类IoC容器),本次的笔记是关于VS里面提供的Unit Testing工具的使用以及Moq(模拟工具)。

1.Visual Studio自带的单元测试工具

除了使用微软自带的单元测试工具,我们还可以选择NUnit--非常流行的一款测试工具。接下来我们创建一个项目ProductApp,你也可以使用NUnit,猛击这里获取。它的使用跟VS自带的非常类似。

首先我们创建用来的测试的类和接口,如下所示:

复制代码
public class Product 
{

public int ProductID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public string Category { set; get; }
}

public interface IProductRepository
{

IEnumerable<Product> GetProducts();
void UpdateProduct(Product product);
}

public interface IPriceReducer
{

void ReducePrices(decimal priceReduction);
}
复制代码

这里的Product类跟前面笔记里面的是一样的,IProductRepository接口定义两个方法:分别用来获取和更新。这个符合Repository模式,我们在前面的笔记里面介绍的。IPriceReducer定义了一个具体的降价方法,针对所有的Products。我们的目的就是要实现这个接口并满足下面的条件:

a.所有的Products都要降价  

b.总的降价必须等于所有的Products数量与降价数额的乘积  

c.Repository的UpdateProduct方法应该能被每一个product调用

d.降价后所有的Products的价格不能低于1

接下来我们添加一个实现IProductRepository的类,如下所示:

复制代码
public class FakeRepository : IProductRepository 
{
private Product[] products = {
new Product() { Name = "Kayak", Price = 275M},
new Product() { Name = "Lifejacket", Price = 48.95M},
new Product() { Name = "Soccer ball", Price = 19.50M},
new Product() { Name = "Stadium", Price = 79500M}
};

public IEnumerable<Product> GetProducts()
{
return products;
}

public void UpdateProduct(Product productParam)
{
foreach(Product p in products
.Where(e => e.Name == productParam.Name)
.Select(e => e)) {
p.Price = productParam.Price;
}
UpdateProductCallCount++;
}

public int UpdateProductCallCount { get; set; }

public decimal GetTotalValue()
{
return products.Sum(e => e.Price);
}
}
复制代码

如果你跟我一样对3.0里面出现的查询表达式LINQ不熟悉的话,我建议你花时间看下相关的资料,前面5天我在补习这方面的知识,为了方便学习MVC,而且了解了LINQ以后你会慢慢喜欢LINQ的。下面接着是实现IPriceReducer接口,如下所示:

复制代码
public class MyPriceReducer : IPriceReducer 
{
private IProductRepository repository;

public MyPriceReducer(IProductRepository repo)
{
repository = repo;
}

public void ReducePrices(decimal priceReduction)
{
//这里先不去实现
throw new NotImplementedException();
}
}
复制代码

这里我们又可以看到DI,而且是通过构造器参数来实现依赖注入的(如果你比较对DI比较生疏,可以看看我前面做的笔记)。

下面我们开始进入测试的正题了,创建一个测试项目。方法很简单,下图是一种:

在我们刚才的那个没有具体实现的方法上右键创建单元测试即可。接下来的操作如图:

单元测试是在一个单独的项目里面,这里我们没有创建所以会弹出创建该测试项目的对话框,我们给它取名为:ProductApp.Tests.创建完成后会自动创建一个MyPriceReducerTest.cs类。里面默认会生成一些方法,我们更改如下:

复制代码
namespace ProductApp.Tests 
{

[TestClass]
public class MyPriceReducerTest
{
//测试是否是所有的价格都会发生改变
[TestMethod]
public void All_Prices_Are_Changed)
{

// Arrange
FakeRepository repo = new FakeRepository();
decimal reductionAmount = 10;
IEnumerable<decimal> prices = repo.GetProducts().Select(e => e.Price);
decimal[] initialPrices = prices.ToArray();
MyPriceReducer target = new MyPriceReducer(repo);

// Act
target.ReducePrices(reductionAmount);
//合并两个序列(4.0里面新提供的扩展方法)
//这里是将开始的价格跟降价后的价格逐一对比,如果有相等的说明降价失败。        
prices.Zip(initialPrices, (p1, p2) => {
if (p1 == p2) {
Assert.Fail();
}
return p1;
});
}
}
}
复制代码

这里我们可以发现测试方法上面的会标记一些特性(Attributes),如[TestMethod],[TestClass]。如果测试的项目里面的类没有包括这些特性的类和方法是不会测试的,VS会忽略它们。我们可以发现是按照arrange/act/assert的模式来进行单元测试的。arrange:初始化测试的环境属于准备阶段;act:执行测试;assert:断言,测试的结果。对于测试方法的约定有很多种,我们这里的是简单的,通过方法名就能够知道测试的内容是什么。其实如果你不喜欢大可以选择自己的方式或者是团队的约定,主要是为了开发人员能够理解。

关于创建单元测试有很多不同的方式,一种通常的方式就是在一个方法里面测试某一功能的所有情况。这里我们倾向将很多不同的情况分散到一个一个小的测试方法里面来进行。这种通过测试不断来完善我们的代码的开发模式成为TDD(测试驱动开发).接着创建测试方法,代码如下:

复制代码
[TestMethod] 
public void Correct_Total_Reduction_Amount()
{

// Arrange
FakeRepository repo = new FakeRepository();
decimal reductionAmount = 10;
decimal initialTotal = repo.GetTotalValue();
MyPriceReducer target = new MyPriceReducer(repo);

// Act
target.ReducePrices(reductionAmount);

// Assert
Assert.AreEqual(repo.GetTotalValue(),(initialTotal - (repo.GetProducts().Count() * reductionAmount)));
}

[TestMethod]
public void No_Price_Less_Than_One_Dollar()
{

// Arrange
FakeRepository repo = new FakeRepository();
decimal reductionAmount = decimal.MaxValue;
MyPriceReducer target = new MyPriceReducer(repo);

// Act
target.ReducePrices(reductionAmount);

// Assert
foreach (Product prod in repo.GetProducts()) {
Assert.IsTrue(prod.Price >= 1);
}
}
复制代码

上面的两个测试方法应该好理解吧。这里同样实现了依赖注入,而且是构造器注入。关于Assert静态方法还有很多,我们可以猛击这里了解。每一个静态方法让我们针对单元测试的一个方面来测试,如果Assert失败就会抛出一个异常,这也就意味着整个单元测试失败。每一个单元测试是独立的,其他的单元测试不会因为某一个单元测试的失败而停止。

Assert的每一个静态方法都有一个重载的带有string参数的方法,这个参数包含了Assert失败时的异常信息。值得注意的是这个ExceptionExpected的特性,这是一个断言,只有当单元测试通过异常类型的参数抛出一个具体化的类型的异常才成功。这是一种非常灵巧的方式,它确保了抛出异常不需要在我们的测试代码里面混入try...catch.

接下来我们运行测试,这时会报错,因为我们有个方式还没实现呢?接着实现该方法,代码如下:

复制代码
public class MyPriceReducer : IPriceReducer 
{
private IProductRepository repository;

public MyPriceReducer(IProductRepository repo)
{
repository = repo;
}

public void ReducePrices(decimal priceReduction)
{
foreach (Product p in repository.GetProducts())
{
p.Price = Math.Max(p.Price - priceReduction, 1); //既能降价而且可以保证每个Product的价格不低于1元
repository.UpdateProduct(p);
}
}
}
复制代码

再一次请大家注意这里的加黑部分实现了构造器注入的。

好了,本次的笔记就到这里。今天的主要介绍了VS自带的测试工具,比较容易理解。本来没打算记这部分的笔记,但为了保持整个笔记的连贯,还是写了。如果你已经对测试比较熟悉了,抱歉浪费你时间了。后面还有一个Moq工具的介绍,之后会有一个小的应用:SportsStore.

笔记中肯定会有我理解不准确或错误的地方,还请路过的朋友多多指导帮助,谢谢!

晚安!

本文转自Rt-张雪飞博客园博客,原文链接http://www.cnblogs.com/mszhangxuefei/archive/2011/12/18/mvcnotes_10.html如需转载请自行联系原作者


张雪飞

相关文章
|
3月前
|
开发框架 前端开发 JavaScript
ASP.NET MVC 教程
ASP.NET 是一个使用 HTML、CSS、JavaScript 和服务器脚本创建网页和网站的开发框架。
45 7
|
3月前
|
存储 开发框架 前端开发
ASP.NET MVC 迅速集成 SignalR
ASP.NET MVC 迅速集成 SignalR
74 0
|
4月前
|
开发框架 前端开发 .NET
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
54 0
|
4月前
|
开发框架 前端开发 安全
ASP.NET MVC 如何使用 Form Authentication?
ASP.NET MVC 如何使用 Form Authentication?
|
4月前
|
开发框架 .NET
Asp.Net Core 使用X.PagedList.Mvc.Core分页 & 搜索
Asp.Net Core 使用X.PagedList.Mvc.Core分页 & 搜索
136 0
|
7月前
|
开发框架 前端开发 .NET
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
210 0
|
7月前
|
开发框架 前端开发 JavaScript
JavaScript云LIS系统源码ASP.NET CORE 3.1 MVC + SQLserver + Redis医院实验室信息系统源码 医院云LIS系统源码
实验室信息系统(Laboratory Information System,缩写LIS)是一类用来处理实验室过程信息的软件,云LIS系统围绕临床,云LIS系统将与云HIS系统建立起高度的业务整合,以体现“以病人为中心”的设计理念,优化就诊流程,方便患者就医。
83 0
|
7月前
|
开发框架 前端开发 .NET
C# .NET面试系列六:ASP.NET MVC
<h2>ASP.NET MVC #### 1. MVC 中的 TempData\ViewBag\ViewData 区别? 在ASP.NET MVC中,TempData、ViewBag 和 ViewData 都是用于在控制器和视图之间传递数据的机制,但它们有一些区别。 <b>TempData:</b> 1、生命周期 ```c# TempData 的生命周期是短暂的,数据只在当前请求和下一次请求之间有效。一旦数据被读取,它就会被标记为已读,下一次请求时就会被清除。 ``` 2、用途 ```c# 主要用于在两个动作之间传递数据,例如在一个动作中设置 TempData,然后在重定向到另
360 5
|
7月前
|
开发框架 前端开发 .NET
进入ASP .net mvc的世界
进入ASP .net mvc的世界
|
存储 开发框架 前端开发
[回馈]ASP.NET Core MVC开发实战之商城系统(五)
经过一段时间的准备,新的一期【ASP.NET Core MVC开发实战之商城系统】已经开始,在之前的文章中,讲解了商城系统的整体功能设计,页面布局设计,环境搭建,系统配置,及首页【商品类型,banner条,友情链接,降价促销,新品爆款】,商品列表页面,商品详情等功能的开发,今天继续讲解购物车功能开发,仅供学习分享使用,如有不足之处,还请指正。
172 0