• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

【ASP.NetMVC3】使用Moq让单元测试变得更简单

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

前几天调查完了unity。现在给我的任务是让我调查Moq。

以下是自己找了资料,总结并实践的内容。如果有表述和理解错误的地方。恳请指正。

什么是Moq?

 

Moq(英语发音是Mock-you 或者只是mock)是一个针对.Net开发的模拟库,它从开始就完全充分利用了.NET3.5(LINQ表达式树)和C#3.0的新特性(lambda表达式)。它的目标是让模拟以一种自然的方式与现有单元测试进行集成,使它更加简单、直观,以避免开发人员被迫重写测试或高成本的学习测试框架。这使它成为了一个高生产力、类型安全、重构友好的模拟库。

从哪得到Moq?

 

如果你看过我的其他文章,我们可以直接使用 VS中的插件Nuget来获取Moq并且引用到指定的项目。

否则,我们可以从http://code.google.com/p/moq/这里得到Moq的最新版本。

可以模拟什么?局限性

 

首先,模拟的类不能是密封的。

其次,你不能直接模拟静态方法。因为Moq只能创建模拟对象实例。在这种情况下,间接的解决方案是我们可以在要模拟对象外包装一层,并且去模拟这个新对象。这种模式被称为适配器模式。

通常我们测试一个方法,它有可能调用好几个service。但是每次都去访问这些service的代价是很高的。我们可以通过模拟的方法让它模拟访问service,并且根据不同请求模拟返回响应的结果。

Moq原理

 

Moq是如何办到的?它只需要一个接口类型就可以生产一个对象?没错,就是这样。Moq使用 Castle DynamicProxy 完成这个任务。基本原理就是它利用反射机制的 Emit 功能动态生成一个空类型(也就是所有接口的方法都实例化,但是没有任何功能,只是一个程序骨架)。所以Mock的能力就在于可以利用DynamicProxy的机制快速生产出一个假对象来,用于模仿真对象的行为。

 

Moq中的重要成员

 

Mock

通过这个类,我们可以得到一个Mock<T>对象。T可以是接口,也可以是类。它有一个public 和virtual属性。让我们看看下边的例子:

        //define interface to be mocked

public interface IFake
{

bool DoSomething(string actionname);

}

//define the test method

[TestMethod]

public void Test_Interface_IFake()
{

//make a mock Object by Moq

var mo = new Mock<IFake>();

//Setup our mock object

mo.Setup(foo => foo.DoSomething("Ping"))

.Returns(true);

//Assert it!

Assert.AreEqual(true, mo.Object.DoSomething("Ping"));
}

在上边的代码,我们通过传递泛型参数IFake去创建Mock<IFake>的实例 模拟接口IFake。

接下来我们要调用Setup()方法去创建我们的模拟对象。注意,Setup方法的参数是一个lambda表达式。我们可以这样理解:当被模拟的对象foo调用它自己的方法DoSomething(),并且参数是Ping。添加后缀 Return (true)我们可以理解为:前边的请求返回结果为真。这是我们指定的返回值。当一个请求调用DoSomething()方法时。如果传入的参数是Ping,那么我们会返回true。接下来,我们添加一个断言,去判断是否能得到预期结果。

注:Foo仅仅是一个词用作通用替代真实的东西,特别是在讨论技术想法和问题.

It

 

这是一个静态类,定义了静态的泛型方法:Is<TValue>, IsAny<TValue>, IsInRange<TValue>, 和IsRegex。去过滤参数。看看下边例子:

public interface IEmailSender
{

bool Send(string subject, string body, string email);

}
[TestMethod]

public void User_Can_Send_Password()
{

var emailMock = new Mock<IEmailSender>();

emailMock

.Setup(sender => sender.Send(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))

.Returns(true);

}

任何时候调用Send()方法,只要传入的参数是任何的string,我们定义他会返回true。

我们也可以根据lambda的优势订制一个规则:

          

  var productRepository = new Mock<IProductRepository>();

productRepository

.Expect(p => p.Get(It.Is<int>(id => id > 0 && id < 6)))

.Returns(newProduct.Object);

 

这样我们可以设置这个id在0和6之间的时候才会返回一个新的对象。上边提及到的其他方法,我们可以参考这里的教程 Moq’s QuickStart

此外,这个类的增强版是 Match<T>,你完全可以自定义模拟规则。

MockBehavior

 

这个类用于模拟对象的行为。就像是否按照默认的模式去模拟。让我们进一步看看他的定义:

namespace Moq
{
// Summary:
// Options to customize the behavior of the mock.
public enum MockBehavior
{
// Summary:
// Causes the mock to always throw an exception for invocations that don't have
// a corresponding setup.
Strict = 0,
//
// Summary:
// Will never throw exceptions, returning default values when necessary (null
// for reference types, zero for value types or empty enumerables and arrays).
Loose = 1,
//
// Summary:
// Default mock behavior, which equals Moq.MockBehavior.Loose.
Default = 1,
}
}

 

现在,看看如下例子:

 

var mock = new Mock<IFake>(MockBehavior.Strict);

指定了mock行为是精准的,如果没有按照预期的Setup就会抛出异常。

MockFactory

 

这是一个模拟对象的工厂,我们不仅仅可以定制创建模拟对象的配置,也可以成批测试它们。看看下边例子:

 

var factory = new MockFactory(MockBehavior.Strict) { DefaultValue = DefaultValue.Mock };

// Create a mock using the factory settings

var fooMock = factory.Create<IFake>();

// Create a mock overriding the factory settings

var barMock = factory.Create<IEmailSender>(MockBehavior.Loose);

// Verify all verifiable expectations on all mocks created through the factory

factory.Verify();

在前边,我们已经介绍了传统方式的创建模拟对象,它不会去真正的调用方法,而是仅仅去执行一些假设:如果...那么返回... 。

在下边的例子里,我将继续介绍一些Mock<T> 类中基本并且重要的方法。

Verification

 

有时候,我们要确定一个方法是否被调用了,或者甚至要知道它被调用了多少次。一个比较传统的方式是使用Verify()方法。看看下边例子:

mock.Verify(foo => foo.DoSomething("Ping"), Times.Once());

上边的代码尝试验证DoSomething("Ping")需要被调用,并且只调用一次。出了Once选项,这里也有更多的选项可供你选择去决定这个方法需要被调用多少次。如: AtLeast, AtLeastOnce, AtMost, AtMostOnce, Between, Equals, Exactly, Never, 和Once

一旦我们已经模拟了对象,验证将是个轻松的任务,看看下边的例子:

   [TestMethod]

public void Test_FindByName_GetCalled()
{

// create some mock data

IList<Product> products = new List<Product>

{

new Product { ProductId = 1, Name = "C# Unleashed",

Description = "Short description here", Price = 49.99 },

new Product { ProductId = 2, Name = "ASP.Net Unleashed",

Description = "Short description here", Price = 59.99 },

new Product { ProductId = 3, Name = "Silverlight Unleashed",

Description = "Short description here", Price = 29.99 }

};

Mock<IProductRepository> mock = new Mock<IProductRepository>();

//mock

//.Setup(sender => sender.FindById(It.IsAny<int>()))

//.Returns((int s) => products.Where(

// x => x.ProductId == s).Single());

mock.Object.FindById(1);

mock

.Verify(x => x.FindById(1), Times.Once());

}
}

在上边的例子里,有两个地方值得注意。

首先是mock.Object.FindById(1)。为了在这个case里让一切变得简单,我们直接调用mock.Object的方法。为什么呢?因为我们这个case只关注这个方法被调用的次数,而不关注返回值。当然,在实际的应用中我们很少这样做。

第二,你有没有注意到被注释掉的句子。它仅仅是一个“如果,那么”句子。意思是说,如果Setup 好了,就返回我们定义的值。

由于调查的时间有限,还有很多关于Mock给力的地方我没有涉及到。我们可以去查看官方的资料。欢迎共同讨论。

 

 

参考资料

http://stephenwalther.com/blog/archive/2008/06/12/tdd-introduction-to-moq.aspx

http://blog.miniasp.com/post/2010/09/16/ASPNET-MVC-Unit-Testing-Part-03-Using-Mock-moq.aspx
http://dotnetslackers.com/articles/aspnet/Built-in-Unit-Test-for-ASP-NET-MVC-3-in-Visual-Studio-2010-Part-2.aspx#s4-using-moq-framework

 

 
摘要: Moq是一个针对.Net开发的模拟库,它的目标是让模拟以一种自然的方式与现有单元测试进行集成,使它更加简单、直观,以避免开发人员被迫重写测试或高成本的学习测试框架。阅读全文
posted @ 2012-01-10 12:54 技术弟弟 阅读(1849) | 评论 (11) 编辑
 
摘要: Unity是一个轻量级的可扩展的依赖注入容器,支持构造函数,属性和方法调用注入构建一个成功应用程序的关键是实现非常松散的耦合设计。松散耦合的应用程序更灵活,更易于维护。这样的程序也更容易在开发期间进行测试。你可以模拟对象,具有较强的具体依赖关系的垫片(轻量级模拟实现),如数据库连接,网络连接,ERP连接,和丰富的用户界面组件。例如,处理客户信息的对象可能依赖于其他对象访问的数据存储,验证信息,并检查该用户是否被授权执行更新。依赖注入技术,可确保客户类正确实例化和填充所有这些对象,尤其是在依赖可能是抽象的 。阅读全文

摘要: 你的薪水和你的职位成正比么?薪水的一直是让我们最纠结的。没有人不喜欢钱。你积极努力的工作,得到身边同事的认可。但或多或少觉得得到的和付出的不成正比。如果你觉得赞同。请继续往下看。你在一个什么样的公司?因为是围绕薪水一个话题,我把公司分成需要自己谈和不需要自己谈的公司。不需要谈薪水的公司:世界500强的公司一般不需要谈薪水。相对会受到公平的待遇。这种公司对职位的分级很严格。尽管有差异,但是薪资范围是每个级别的上限和下限。这样的公司每年都会有固定的按比例的工资涨幅。如果你没有在以上类型的公司,请继续往下看。找谁谈判?每个公司体系不一样。一般是去找自己的薪资经理。如果是小公司,那就去找你的老板吧!究阅读全文
posted @ 2011-12-22 08:20 技术弟弟 阅读(9763) | 评论 (114) 编辑
 
摘要: 如果你浏览一些最流行的网站。你会发现实际上他们有两个版本。一个用于桌面浏览器(完整布局),另一个是优化移动(触摸集中)。某些情况下,在传统的网站上也可以使用悬停界面来提高用户的体验,他们可以在特定的区域类浏览更多的内容。阅读全文
posted @ 2011-12-20 12:02 技术弟弟 阅读(2985) | 评论 (26) 编辑
 
摘要: 在本教程中,我们将创建一个独特的滑动框导航。这样做可以让有菜单的盒子滑出,并且弹出缩略图。在某些菜单项中我们还包含着有进一步链接的子菜单。取决于我们鼠标在菜单项上的停悬,子菜单将向左或向右滑动。我们将使用jQuery Easing Plugin插件和一些由tibchris.提供的漂亮图片标记在HTML的结构中,我们将使用一个无序的列表,其中每个菜单项将包含的主要链接和一个子菜单的div元素:<ul >阅读全文
posted @ 2011-12-19 14:37 技术弟弟 阅读(2630) | 评论 (29) 编辑
 
摘要: 问题随着您网站的成长,无论是普通的还是动态的内容,这两个因素导致网站载入变慢。许多用户造成了大量的web 服务器和数据库的请求。大量的数据需要强大的数据库处理能力来支持它。为了防止花了很多钱,只需添加更多的Web服务器,更聪明的编程,以减少不必要的数据库或动态处理请求,可以明显增加你的web应用的整体速度解决方案使用OutputCacheAttribute去缓存那些不经常改变或只在具体action改变的数据。讨论在MVC3中缓存是非常容易的。简单的添加下边的attribute在一个controller的一个action上。[OutputCache (Duration=600)]这将自动缓存视图阅读全文
posted @ 2011-12-19 10:37 技术弟弟 阅读(1599) | 评论 (16) 编辑
 
摘要: 使用RouteCollectionExtensions 类下的MapRoute 函数去生成更友好的名字去展示URL。阅读全文
posted @ 2011-12-16 15:25 技术弟弟 阅读(1001) | 评论 (1) 编辑
 
摘要: 本节介绍使用JQuery autocomplete 实现自动完成以及使用renderSection 来按需要加载css和javascript文件阅读全文
posted @ 2011-12-15 13:56 技术弟弟 阅读(1203) | 评论 (2) 编辑
 
摘要: 在用ipad 装应用程序的时候,经常在AppStore里精挑细选。觉得他们的翻页做的很不错,按需取数据。 这篇文章是讲解如何使用Asynchronous controllers 配合AJAX 实现卷帘式分页。阅读全文
posted @ 2011-12-15 10:13 技术弟弟 阅读(1385) | 评论 (16) 编辑
 
摘要: 使用JQuery Mobile 让你的网站在移动设备上同样精彩!阅读全文
posted @ 2011-12-13 17:24 技术弟弟 阅读(1204) | 评论 (7) 编辑
 
摘要: 使用CAPTCHA-全自动区分计算机和人类的图灵测试去防止恶意软件自动添加评论。阅读全文
posted @ 2011-12-07 19:42 技术弟弟 阅读(916) | 评论 (3) 编辑
 
摘要: 使用MVC 的 AjaxHelper 提交表单。 当model 发生改变时,调用我们实现的BookInitializer删除并创建新的数据库。阅读全文
posted @ 2011-12-05 09:08 技术弟弟 阅读(1392) | 评论 (11) 编辑
 
摘要: MVC 3 提供了3个新的helper类 :HtmlHelper,URLHelper,和AjaxHelper。 这节我们将使用 AjaxHelper 来提高用户体验。阅读全文
posted @ 2011-12-01 15:05 技术弟弟 阅读(1290) | 评论 (5) 编辑
 
摘要: 使用 FileStream, Image, Bitmap,和Graphics 类去生成缩略图。阅读全文
posted @ 2011-12-01 08:12 技术弟弟 阅读(2456) | 评论 (37) 编辑

 

 
摘要: 通过HttpPostedFileBase.实现上传文件。阅读全文
posted @ 2011-11-30 22:35 技术弟弟 阅读(1121) | 评论 (2) 编辑
 
摘要: 问题当排序和分页和过滤都不够帮用户去找到他们想要的结果时,想一个最好的备选方式是让用户输入(关键字)他们想要什么。解决方案用HtmlHelper创建一个新的From和 文本输入框,并且借助LINQ根据用户输入的关键字在之前过滤过的结果里查找。讨论和前边的秘方很像,添加一个根据keyword 搜索的功能需要更新 Book/Index view 和 BookController。在View里添加一个新的from和textbox 让用户输入keyword。同时也要确保当用户改变排序规则、过滤、分页时,关键字保持。下边的代码是对View的更新:@model PagedList.IPagedList&l阅读全文
posted @ 2011-11-30 17:57 技术弟弟 阅读(1049) | 评论 (0) 编辑
 
摘要: 当排序和分页都不够帮用户去找到他们想要的结果时,另外一种帮助用户找到他们想要的结果的方式是根据特殊的规则过滤。阅读全文
posted @ 2011-11-30 14:03 技术弟弟 阅读(1143) | 评论 (1) 编辑
 
摘要: 我们将使用 PagedList.MVC 为列表结果分页。它是一个通用的分页控件。你可以从NuGet 获得它。阅读全文
posted @ 2011-11-29 16:31 技术弟弟 阅读(1181) | 评论 (6) 编辑
 
摘要: 问题你有一个很庞大的列表(例如,图书列表),你不能很容易找到你想找的东西。以列表中某一列为基础排序,可以帮助你更快的去找到你想要的东西。解决方案在book list的标题上添加一个链接。当用户点击链接的时候,使用Dynamic Linq Library去为结果排序,给予选择的列。(升序或者降序)。再点一次链接的话,就会反转顺序。讨论和以前我用过的框架相比较,我对于在自动生成的View里添加一个排序有点惊讶。希望在未来版本的MVC中,脚手架可以帮我们去做这件事。另一件我需要做的事就是在ASP.NET MVC 的主页上提供一个或更多的选项可以去切换排序。在图书的例子里,只有5个列需要被排序,也不算阅读全文
posted @ 2011-11-28 17:32 技术弟弟 阅读(1672) | 评论 (8) 编辑
 
摘要: 我也是翻译完第六章才思考是否有必要来写出目录的。我一开始也觉得这仅仅是20个知识点。但是知道我真正看完这本书的时候,我才了解到,这是一个从0开始的小项目。从这20个秘方的第1个一直到20个,正好可以完整完成一个MVC的项目。虽然不一个大的项目,但是也包含了很多必要的知识点。麻雀虽小五张俱全。验证,排序,分页,多语言,搜索,上传,ajax,缓存,移动电话访问的站点这些都是主流的功能。这可以引导一个想真正用MVC实践的朋友来完成他的第一个MVC项目。以下是目录:1.1 用密码保护限制对view的访问。1.2 用脚手架(entity framework)生成view 和controller。1.3 阅读全文
posted @ 2011-11-27 23:34 技术弟弟 阅读(314) | 评论 (0) 编辑
 
摘要: 问题你网站的一个用户在你的网站已经注册了,但是他忘记了密码,现在需要一种方式去找回他。解决方案为了允许用户去找回他们的密码,必须在AccountController中添加一个新的action和一个新的view。这个功能将使用MemberShip类去寻找一个匹配的用户,并发送一个包含它密码的邮件到他们相关的邮箱。讨论默认情况下,MVC Internet Applications 使用的是单向 hash为密码加密。这样,密码不可能被找回。在下边的例子。默认的加密方法使用双向加密。这样虽然不是很安全。但是他避免了强迫那些忘记了密码的用户重置密码。作为开始,我们首先要修改web.config中关于me阅读全文
posted @ 2011-11-27 22:00 技术弟弟 阅读(1189) | 评论 (3) 编辑
 
摘要: How to make love---HTML5 in 5 minutes阅读全文
posted @ 2011-11-24 17:04 技术弟弟 阅读(1587) | 评论 (8) 编辑
 
摘要: 场景很多网站要求人们先注册再去访问内容或者发表评论.网站如牛毛,怎么可能让人们记住每个他们注册过的网站。在注册的过程中,可以发送一个电子邮件来提醒用户他们刚刚注册了,这样,他们可能一会还会返回到你的网站。解决方案在用户注册之后使用SmtpClient和MailMessage发送邮件通知。讨论发送一个邮件之前,你需要配置一个SMTP服务器,端口,用户名和密码。为了使配置简单化,我建议你在web.config的appsetting中配置。<appSettings><add key="webpages:Version" value="1.0.0.0&q阅读全文
posted @ 2011-11-23 20:25 技术弟弟 阅读(1381) | 评论 (2) 编辑
 
摘要: 场景互联网是由千百万个人使用。他们来自数百个不同的国家,使用数百种不同的语言。甚至在加拿大、美国和英国之间英语也有许多方言。所以,不在你的网站上仅仅呈现一种语言是重要的。解决方案创建资源文件,并且以键值对的形式添加静态文本,通过CurrentUICulture来提供改变语言的能力。讨论资源文件是基于文本的XML文件,用来支持静态网站多国语言。你创建一个主要的资源文件,包含你的默认语言。为文本创建一些key/value pair。然后,你就可以在你的controller、view、model中的任何地方使用这些文本了。下图是个例子注意圈红的地方要设置为public。如果一个文件的访问修饰符不是p阅读全文
posted @ 2011-11-23 15:41 技术弟弟 阅读(1580) | 评论 (4) 编辑
 
摘要: 场景你要确保你的表单捕获的数据包含你预期的数据,这些数据是基于你的数据库或模型设计。解决方案.NET 4.0 包含了一个新的数据注解命名空间,提供了一些有用的元数据属性类。这些类已经被应用到MVC3。对于验证表单输入,下面的属性类可以用来提供各种各样验证选项:RequiredAttribute,RegularExpressionAttribute,RangeAttribute和DataTypeAttribute。当需要自定义的验证的时候,MVC的3还支持改进ValidationAttribute类,允许开发人员定义的验证。讨论接下来的例子是要去扩展“code-first book“model,阅读全文
posted @ 2011-11-23 11:15 技术弟弟 阅读(1732) | 评论 (2) 编辑
 
摘要: 场景你要通过你的网站管理动态内容解决方案通过使用脚手架自动生成一个controller和多个views 允许用户Create, Read, Update, and Delete(CRUD)数据。通过EntityFramework 的 code-first(代码先行) and database-first (数据库先行)方法。讨论在用脚手架搭建contorller和view之前,一个model和DBContext需要被创建。它将定义model存了什么类型的数据。(这是代码先行的方法)在接下来的例子里。创建的2个类将提供管理一个图书列表的能力。第一个类包含图书数据的定义,将保存在SQL Expre阅读全文
posted @ 2011-11-22 18:22 技术弟弟 阅读(1376) | 评论 (8) 编辑
 
摘要: 场景你想阻止用户访问你网站的特定页面,除非用户已经注册并且使用了用户名和密码登陆。<!--[if !supportLineBreakNewLine]-->解决方案使用一个AccountController,AccountModels 和 几个MVC View,配合ASP.NET的 AuthorizeAttribute 特性,FormsAuthentication和Membership creation/validation 讨论微软的MVC团队已经对账户controller做了很多的改进。它已经被更新用于Form验证,连同Membership 类去创建新的用户,验证存在的用户,创建阅读全文
posted @ 2011-11-22 15:06 技术弟弟 阅读(1616) | 评论 (15) 编辑

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
使用ASP.Net WebAPI构建REST服务(五)——客户端发布时间:2022-07-10
下一篇:
ASP.NET Core 3.1 WebAPI 自定义ActionFilter过滤器发布时间:2022-07-10
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap