在上一篇文章《我的TDD实践:可测试性驱动开发(上)》里,我谈到自己在采用传统TDD方式进行开发时感到有些尴尬,最后不得不放弃这种先写测试再写代码最后重构的方式。不过我还是非常注重单元测试的实践,慢慢发现自己的做法开始转向另一种TDD方式,也就是“可测试性驱动开发”。简单的说,我现在采取的做法是,先开发,再测试,一旦发现产品代码不太容易测试,则将其重构为容易测试的代码。我发现,这种时刻注重可测试性的开发方式,其最终也能够得到质量较高的代码。例如,它和SOLID原则也颇为融洽。上次谈的比较理论,而这次我便通过一个简单功能的开发过程,来表现我的思维方式及常用做法。
任务描述
这个功能是开发ASP.NET MVC项目时的常见任务:构建一个Model Binder。ASP.NET MVC中Model Binder的职责是根据请求的数据来生成Action方法的参数(即构建一个对象)。那么这次,我们将为负责产品搜索的Action方法提供一个 SearchCriteria参数作为查询条件:
public class SearchCriteria { public PriceRange Price; public string Keywords {get;set; } public Color Colors {get;set; } } [Flags] public enum Color { Red = 1, Black = 1 << 1, White = 1 << 2 } public class PriceRange { public float Min { get; set; } public float Max { get;set; } } |
SearchCriteria中包含三个条件,一是复杂类型的Price条件,二是字符串类型的Keywords条件,三是一个Color枚举类型。作为查询条件,它总是需要在URL中表示出来的。例如,如果是这样的URL:
/keywords-hello%20world--price-100-200--color-black-red |
它表示的便是这样的条件:
* 价格为100到200之间
* 关键字为“hello world”(注意URL转义)
* 颜色为黑或红(使用Color.Black | Color.White表示)
而最终,我要使用“可测试性驱动开发”来实现的便是这个方法:
public class SearchCriteriaBinder:IModelBinder { public object BindModel(ControllerContext controllerContext,ModelBindingContext bindingContext) { throw new NotImplementedException(); } } |
那么,我又会怎么做呢?