DI容器、单元测试框架、模仿工具
一、Ninject
Ninject的目的:解决对接口的实现进行实例化,但需要的实现细节又不在控制器代码中。
Ninject使用C#的类型参数创建了一种关系:将想要使用的接口设置为Bind方法的类型参数,并在返回的结果上调用To方法。
笔记:
1.Ninject条件绑定常用方法:
When(predicate) //当predicate结果为true时,实施绑定。(predicate一个lambda表达式)
WhenclassHas<T>() //当被注入的类以注解属性进行注释,且其类型为T时,实施绑定([T])
WhenInjectedInfo<T>() //当要被注入的类是类型T时,实施绑定
2.使用Ninject:
1.创建依赖解析器(Infrastructure)
using System; using System.Collections.Generic; using System.Web.Mvc; using Essentialtools.Models; using Ninject; namespace Essentialtools.Infrastructure { public class NinjdectDependencyResolver:IDependencyResolver { private IKernel kernel; public NinjdectDependencyResolver() { //创建Ninject内核的实例 kernel = new StandardKernel(); AddBindings(); } public object GetService(Type serviceType) { return kernel.TryGet(serviceType); } public IEnumerable<object> GetServices(Type serviceType) { return kernel.GetAll(serviceType); } //建立接口与实现类之间的关系 private void AddBindings() { //构造器 kernel.Bind<IValueCalculator>().To<LinqValueCalculator>(); //属性 // kernel.Bind<IDiscoutnHelper>().To<DefaultDiscoutnHelper>().WithPropertyValue("DiscountSize",20M); //构造器参数 kernel.Bind<IDiscoutnHelper>().To<DefaultDiscoutnHelper>().WithConstructorArgument("discountParam", 30M); //条件绑定 kernel.Bind<IDiscoutnHelper>().To<FlexibleDiscountHelper>().WhenInjectedInto<LinqValueCalculator>(); } } }
2.注册依赖解析器(Global.asax)
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Routing; using Essentialtools.Infrastructure; namespace Essentialtools { // 注意: 有关启用 IIS6 或 IIS7 经典模式的说明, // 请访问 http://go.microsoft.com/?LinkId=9394801 public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); //注册依赖解析器 DependencyResolver.SetResolver(new NinjdectDependencyResolver()); WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); } } }
3.重构控制器
.................
//构造器 private IValueCalculator calc; public HomeController(IValueCalculator calcParam) { calc = calcParam; } .................
二、单元测试:
引入原命名空间到单元测试中。
“准备动作断言”模式。
静态的Assert方法
方法 | 描述 |
AreEqual<T>(T,T) AreEqual<T>(T,T,string) |
断言两个类型T的对象有相同的值 |
AreNotEqual<T>(T,T) AreNotEqual<T>(T,T,string) |
断言两个类型T的对象的值不相等 |
AreSame<T>(T,T) AreSame<T>(T,T,string) |
断言两个变量指向相同的对象 |
AreNotSame<T>(T,T) AreNotSame<T>(T,T,string) |
断言两个变量指向不同的对象 |
Fail() Fail(string) |
失败断言 -无条件检查 |
Inconclusive() Inconclusive(string) |
指示最终不能建立单元测试的结果 |
IsTrue(bool) IsTrue(bool,string) |
断言一个返回结果的值为True |
IsFalse(bool) IsFalse(bool,string) |
断言一个返回结果的值为false |
IsNull(object) IsNull(object,string) |
断言一个变量未被分配一个对象的引用 |
IsNotNull(object) IsNotNull(object,string) |
断言一个变量被分配了一个对象的引用 |
IsInstanceOfType(object,Type) IsInstanceOfType(object,Type,string) |
断言一个对象是指定的类型,或派生于指定的类型 |
IsNotInstanceOfType(object,Type) IsNotInstanceOfType(object,Type,string) |
断言一个对象不是指定的类型 |
ExpectedException属性成员:只有当单元测试抛出ExceptionType参数指定类型的异常时,该断言才是成功的。不需要try...catch。
[ExpectedException(typeof(ArgumentOutOfRangeException))]
三、Moq:模仿对象
步骤:
1.用Mock创建模仿对象
2.用Setup方法建立模仿对象的行为
3.用It类设置行为的参数
4.用Return指定行为的返回类型
5.用lambda表达式在Return方法中建立具体的行为
Mock<IDiscountHelper> mock=new Mock<IDiscountHelper>();
mock.Setup(m=>m.ApplyDiscount(It.IsAny<decimal>())).Returns<decimal>(total=>total);
var target=new LinqValueCalculator(mock.Object);
It类的静态方法
方法 | 描述 |
Is<T>(predicate) | 指定类型T的值,使“predicate谓词”返回true |
IsAny<T>() | 指定类型T的值为任意值 |
IsInRange<T>(min,max,kind) |
如果参数介于定义值之间,而且是T类型的,则匹配。kind枚举值:Inclusive(包括),Exclusive(排除) |
IsRegex(expr) | 如果参数是一个字符串,而且符合指定的正则表达式则匹配 |
模仿抛出异常:
mock.Setup(m=>m.ApplyDiscount(It.Is<decimal>(v=>v==0))).Throws<System.ArgumentOutRangeException>();
单元测试 这个是思维方法和开发风格决定的。
源码:http://yunpan.cn/cQ9QEgtrWXaS9 访问密码 bbd2