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

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

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

什么是Moq?

 

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

从哪得到Moq?

 

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

【ASP.Net MVC3 】使用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

 

【ASP.Net MVC3 】

 
摘要: 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 id="sdt_menu" class="sdt_menu"> <li><a href="#"> &l 阅读全文
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 让你的网站在移动设备上同样精彩! 【ASP.Net MVC3 】使用Moq让单元测试变得更简单 阅读全文
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)  编辑

 

你可能感兴趣的:(asp.net)