最近有个项目求助,由于开发人员把页面处理的相关逻辑写到了Controller层里,需要增加对Controller层的单元测试。
我看了下Controller层里难于测试的原因主要是httpcontext类难模拟。Controller层用到了session,request信息。
我的解决方法是,在httpcontext与Controller之间增加一个IHttpContextRepository接口,它负责将httpcontext里的内容封装到model里传给Controller。
那么Controller层只和Model层打交道。
定义IHttpContextRepository接口
public interface IHttpContextRepository { LogOnModel GetLogOnModel(HttpContextBase context); string GetAgent(HttpContextBase context); }
public class BaseController : Controller { protected IHttpContextRepository httpContextRepository; public BaseController(IHttpContextRepository prepareControllerVar) { httpContextRepository = prepareControllerVar; } }
在Global.asax中,添加注册组件的代码。
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RegisterGlobalFilters(GlobalFilters.Filters); RegisterRoutes(RouteTable.Routes); // Setup AutoFac var builder = new ContainerBuilder(); DependancySetup(builder); var container = builder.Build(); DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); } protected void DependancySetup(ContainerBuilder builder) { // Controllers builder.RegisterControllers(typeof(MvcApplication).Assembly); builder.RegisterType<HttpContextRepository>().AsImplementedInterfaces(); }
public class HttpContextRepository : IHttpContextRepository { public LogOnModel GetLogOnModel(HttpContextBase context) { LogOnModel LGmodel = new LogOnModel(); LGmodel.UserName = (string)context.Session["NAME"]; LGmodel.Password = (string)context.Session["PASSWORD"]; return LGmodel; } public string GetAgent(HttpContextBase context) { return context.Request.UserAgent; } }
[HttpGet] public ActionResult Top() { ... LogOnModel LGmodel = httpContextRepository.GetLogOnModel(this.HttpContext); ... return View(LGmodel); }
为了方便Mock,引入Moq模块。
使用了Xunit框架
public void TopTest_001() { var mockRepository = new Mock<IHttpContextRepository>(); LogOnModel model = new Models.LogOnModel { UserName = "tom", Password = "12345678" }; //单元测试时,我并不关心HttpContext内的内容,所以放个空的object。我关心的是返回的model mockRepository.Setup(cr => cr.GetLogOnModel(new object() as HttpContextBase)).Returns(model); mockRepository.Setup(cr => cr.GetPhoneFlg(new object() as HttpContextBase)).Returns("IE"); var controller = new AccountController(mockRepository.Object); var result = controller.Top() as ViewResult; Assert.Equal("tom", ((LogOnModel)result.ViewData.Model).UserName); Assert.Equal("12345678", ((LogOnModel)result.ViewData.Model).Password); }