您的位置:首页 > 编程语言 > ASP

[转载] [翻译]在ASP.NET MVC中使用TDD与依赖注入

2009-06-18 18:04 357 查看

此文为转载:http://www.cnblogs.com/darkdawn/archive/2008/09/04/1283692.html 非常好的文章。

(代码截图为ASP.NET MVC
Preview 5版本)

原文地址:http://haacked.com/archive/2007/12/07/tdd-and-dependency-injection-with-asp.net-mvc.aspx

在设计ASP.NET MVC Framework的时候,指导原则之一是要能使用TDD(测试驱动开发)建立web应用程序。本文使用ASP.NET MVC CodePlex Preview 4为例(经过测试,代码可以在Preview 5中运行,翻译版截图全部为Preview 5),我将试着保持这篇文章的内容适用于最新的ASP.NET MVC Framework,但是需要多一点点的时间。

本文提供一个稍具测试驱动开发(TDD)风格的web程序,同时介绍把StructureMap DI(依赖注入)框架集成到这个ASP.NET MVC示例中。在本文结尾处你可以下载代码。

我选择了StructureMap 2.0依赖注入框架,因为我对它比较熟悉,并且它只需要做很少的代码和配置。如果你想把这个示例换成使用Spring.NET,可以到Fredrik Normen’s post查看。以后我可能会写点使用Castle WindsorObjectBuilder的代码示例。

Start Me Up(向滚石乐队致歉)

首先安装好VS2008以及ASP.NET MVC,打开Visual Studio 2008并选择File|New Project,在对话窗口中选择ASP.NET MVC Application模板。



然后选择单元测试项目选择对话框。




默认安装下,只有
Visual Studio Unit Test项目选项可用。但是安装了MbUnit,xUnit等等其他框架也会在这里显示出来。

你可能会猜想,我会从建立一个很权威的blog例子,其实我会从没有数据库的示例开始,我们可以以后慢慢添加。

第一件我想做的事是添加一些类文件到主项目中去。我不会添加任何实现,能编译就行。我首先添加这些:

在Controllers目录下BlogController.cs

在Models目录下IPostRespository.cs

在Models目录下Post.cs

在MvcApplicationTest项目下BlogControllerTests

完成后,我的项目文件树像这样:



现在我想写足够多的代码让我们可以写一个测试。首先,我定义了容器接口。




对真正的博客帖子容器来说这点内容是不够的,但是这里只是做一个
demo而已。当你准备好写一个很强悍的blog引擎的时候,你可以添加更多的方法。

现在我仍然不管Post类,让它继续空着,可以以后再来实现。先实现blog的Controller。




好了,在这里打住。我们已经能够开始写单元测试了。毕竟我准备演示
TDD嘛。让我们先来写个测试。

Let’s Get Test Started, In Here.(向黑眼豆豆致歉)

从最简单的测试开始,确定Recent这个行为(Action)没有指定视图(View),因为我看到默认行为的运行结果。(这段代码假设你已经引用了所有需要的名称空间)



运行这个测试,会失败。




但这正是我们所期望的,因为我们并没有实现
Recent方法。这是TDD红绿重构韵律之红色部分。

还是让我们来把这个方法实现了吧:




注意:我们是把精力集中在行为上,而不是在
UI上。这和使用ASP.NET WebForms是不同的。这两者没有孰优孰劣,只是风格不同而已。

现在当我运行测试,会通过。




很好,现在是
TDD生命周期的绿色部分了!也只个非常非常简单的TDD例子。现在该我们进入介绍依赖注入阶段了。

It’s Refactor Time(向读者致歉,扯得太远了)

为了获得最近的博客帖子,我想为我的博客Controller提供一个“服务”实例,它可以请求这些帖子。

这时,我不能确定我怎么去存储博客帖子,用什么好呢?SQL?XML?还是其他?

都不是。暂时不要去想它吧。

我们可以把这个讨论延迟到最后的时刻。现在我要创建一个抽象容器IPostRepository,用来描述我想怎么存储和取回这些博客帖子。我们来为blogController写些代码,让它可以在它的构造器中接受一个这个接口的实例。

这是依赖注入的依赖(Dependency)部分。这个Controller现在有一个对IPostResitory的依赖。

注入(Injection)部分是一种机制:传递依赖给所需要依赖的类,直接创建类的实例,并绑定类到所指定的接口的实现。

现在修改BlogController类。




很好。注意我并没有改变
Recent方法。我需要先写另一个测试,要确保它传递正确的数据给view。

注意:你现在会发现刚才我们写的测试不能通过,先注释掉,我们一会再修正它。

我们现在要使用mock框架,在我写测试之前,我需要引用Moq.dll到我的测试项目中,在这里下载MoQ

注意:在本示例项目中已经引用了这个程序集。




这个测试动态创建
IPostRepository接口的实现。我们告诉它:不管是什么参数传递给了ListRecentPosts,返回两篇帖子。

注意:我们现在不需要这个接口的实现。我们感兴趣的是把测试Action的逻辑孤立出来,所以在测试的时候我们直接使用了接口实例化。

开始测试,失败了,看来我们需要重构Recent方法,让它正确运行:




重新测试一次,成功了!


Inject That Dependency

当我使用浏览器尝试访问这个action的时候(如http://localhost:14963/Blog/Recent),会出现如下的错误页面:




出现这样的错误很正常,默认情况下,
ASP.NET MVC需要Controller有一个public的、无参的构造器,让它自己可以创建Controller的实例。但是我们的构造器需要一个IPostRepository实例作为参数。我们需要给Controller传递一个这样的参数才行。

StructureMap(或其他依赖注入框架)来救援!

注意:记得下载和引用StructureMap.dll程序集。我在示例代码中已经引用了。

首先要在应用程序根目录下创建StructureMap.config文件,文件内容:




这里不对这个文件内容做详细解释,如果你想了解更多,请查看
StructureMap文档。

它只暴露了你所需要知道的最少细节,每个PluginFamily节点描述一个接口类型和一个节点的键(key)。Plugin节点描述一个具体的类型:框架实例化接口类型需要创建的具体类型。

比如说,第二个PluginFamily节点中,接口类型是IPostRepository,具体的类型是InMemoryPostRepository。当我们使用StructureMap构造一个包含对IPostRepository依赖的类型的实例时,StructureMap会传递一个InMemoryPostReposity实例。

平时我使用SqlPostRepository。但是这个demo的目的并不需要那么做,所以我打算用个静态集合,把存储这些博客帖子到内存中。我们总是可以不急着实现使用SQL版本。

注意:本来应该写一个InMemoryPostRepository的测试,但是这篇文章已经够长了。不过也别担心,我会把单元测试放到示例代码中去。




[b]快,我们需要一个工厂!
[/b]

快完成了。我们需要实现IControllerFactory接口,把StructureMap连接到ASP.NET MVC上。Controller工厂的职责是创建Controller的实例。我们可以在自己的工厂中用这些逻辑:



最后,我们吧他们都连接起来,在Global.asax.cs中的Application_Start方法添加方法调用:



一切搞定!现在我们把依赖注入框架引入到了我们的程序中,我们可以重新访问站点测试一下了(别忘记编译)。我们得到这个页面:




很好很强大!黄屏去死吧,不过在这里是个好现象:我们的依赖对象已经注入到对象中了,这是另一个错误提示,因为我们没有
view创建view造成的。

不好意思,跑题了。

这里我就不做了,你们可以自己做一个,或者你们可以在傻瓜示例源代码中看到。

这个示例有点简单得可笑,不过这种原则在做更大的项目中也是通用的。它只是用来学习技术的,希望在你在TDD路上走得更好。

本文所有内容的源代码下载(ASP.NET MVC Preview 5/StructureMap 2.0)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: