zoukankan      html  css  js  c++  java
  • MVC单元测试,使用Repository模式、Ninject、Moq

    本篇使用Repository设计MVC项目,使用Ninject作为DI容器,借助Moq进行单元测试。

      模型和EF上下文

    模型很简单:

    public class Foo
        {
            public int Id { get; set; }
            public string Name { get; set; }
        }

    EF上下文为:

    using System.Data.Entity;
    
    namespace MvcApplication1.Models
    {
        public class FooBarContext : DbContext
        {
            public DbSet<Foo> Foos { get; set; }
        }
    }

      Repository相关

    为了避免在IXXXRepository中有关增删改查等的重复代码,有必要创建一个所有IXXXRepository的基接口:

    using System;
    using System.Linq;
    using System.Linq.Expressions;
    
    namespace MvcApplication1.Repository
    {
        public interface IBaseRepository<T> where T : class
        {
            IQueryable<T> GetAll();
            IQueryable<T> FindBy(Expression<Func<T, bool>> predicate);
            void Add(T entity);
            void Edit(T entity);
            void Delete(T entity);
            void Save();
        }
    }

    IFooRepository,也可以有自己的接口方法:

    using MvcApplication1.Models;
    
    namespace MvcApplication1.Repository
    {
        public interface IFooRepository : IBaseRepository<Foo>
        {
            Foo GetSingle(int fooId);
        }
    }

    BaseRepository是一个抽象类,提供了所有XXXRepository的泛型基类实现,并实现 IBaseRepository接口:

    using System.Data.Entity;
    using System.Linq;
    
    namespace MvcApplication1.Repository
    {
        public abstract class BaseRepository<C,T> : IBaseRepository<T> where T : class where C : DbContext,new()
        {
            private C _db = new C();
    
            public C Db
            {
                get { return _db; }
                set { _db = value; }
            }
    
            public System.Linq.IQueryable<T> GetAll()
            {
                IQueryable<T> query = _db.Set<T>();
                return query;
            }
    
            public System.Linq.IQueryable<T> FindBy(System.Linq.Expressions.Expression<System.Func<T, bool>> predicate)
            {
                IQueryable<T> query = _db.Set<T>().Where(predicate);
                return query;
            }
    
            public void Add(T entity)
            {
                _db.Set<T>().Add(entity);
            }
    
            public void Edit(T entity)
            {
                _db.Entry(entity).State = EntityState.Modified;
            }
    
            public void Delete(T entity)
            {
                _db.Set<T>().Remove(entity);
            }
    
            public void Save()
            {
                _db.SaveChanges();
            }
        }
    }

    FooRepository不仅派生于BaseRepository<FooBarContext, Foo>,还需要实现IFooRepository约定的接口方法:

    using System.Linq;
    using MvcApplication1.Models;
    
    namespace MvcApplication1.Repository
    {
        public class FooRepository : BaseRepository<FooBarContext, Foo>,IFooRepository
        {
    
            public Foo GetSingle(int fooId)
            {
                var query = GetAll().FirstOrDefault(x => x.Id == fooId);
                return query;
            }
        }
    }

      Ninject控制器工厂

    通过GuGet安装Ninjct,创建Ninject控制器工厂:

    using System.Web.Mvc;
    using MvcApplication1.Repository;
    using Ninject;
    
    namespace MvcApplication1.Extension
    {
        public class NinjectControllerFactory : DefaultControllerFactory
        {
            private IKernel ninjectKernel;
    
            public NinjectControllerFactory()
            {
                ninjectKernel = new StandardKernel();
            }
    
            protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, System.Type controllerType)
            {
                return controllerType == null ? null : (IController) ninjectKernel.Get(controllerType);
            }
    
            private void AddBindings()
            {
                ninjectKernel.Bind<IFooRepository>().To<FooRepository>();
                ninjectKernel.Bind<IBarRepository>().To<BarRepository>();
            }
        }
    }

    并在全局注册:

    protected void Application_Start()
            {
                ......
    
                ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());
            }

      创建FooController,包含增删改查

    using System;
    using System.Web.Mvc;
    using MvcApplication1.Models;
    using MvcApplication1.Repository;
    
    namespace MvcApplication1.Controllers
    {
        public class FooController : Controller
        {
            private readonly IFooRepository _fooRepository;
    
            public FooController(IFooRepository fooRepository)
            {
                _fooRepository = fooRepository;
            }
    
            public ViewResult Index()
            {
                var model = _fooRepository.GetAll();
                return View(model);
            }
    
            public ActionResult Details(int id)
            {
                var model = _fooRepository.GetSingle(id);
                if (model == null)
                {
                    return HttpNotFound();
                }
                return View(model);
            }
    
            public ActionResult Edit(int id)
            {
                var model = _fooRepository.GetSingle(id);
                if (model == null)
                {
                    return HttpNotFound();
                }
                return View(model);
            }
    
            [ActionName("Edit"), HttpPost]
            public ActionResult Eidt_Post(Foo foo)
            {
                if (ModelState.IsValid)
                {
                    try
                    {
                        _fooRepository.Edit(foo);
                        _fooRepository.Save();
                        return RedirectToAction("Details", new { id = foo.Id });
                    }
                    catch (Exception ex)
                    {
                       ModelState.AddModelError(string.Empty, "出错了:" + ex.Message);
                    }
                }
                return View(foo);
            }
    
            public ActionResult Create()
            {
                return View();
            }
    
            [ActionName("Create"), HttpPost]
            public ActionResult Create_Post(Foo foo)
            {
                if (ModelState.IsValid)
                {
                    try
                    {
                        _fooRepository.Add(foo);
                        _fooRepository.Save();
                        return RedirectToAction("Details", new {id = foo.Id});
                    }
                    catch (Exception ex)
                    {
                       ModelState.AddModelError(string.Empty, "出错了:"+ex.Message);
                    }
                }
                return View(foo);
            }
    
            public ActionResult Delete(int id)
            {
                var model = _fooRepository.GetSingle(id);
                if (model == null)
                {
                    return HttpNotFound();
                }
                return View(model);
            }
    
            [ActionName("Delete"),HttpPost]
            public ActionResult Delete_Post(int id)
            {
                var model = _fooRepository.GetSingle(id);
                if (model == null)
                {
                    return HttpNotFound();
                }
                _fooRepository.Delete(model);
                _fooRepository.Save();
                return RedirectToAction("Index");
            }
        }
    }

      单元测试

    通过NuGet安装Moq,借助Moq来模拟接口方法的返回值。

    →初始化

    private IFooRepository fooRepository;
    
            [TestInitialize]
            public void Initialize()
            {
                Mock<IFooRepository> mock = new Mock<IFooRepository>();
                mock.Setup(m => m.GetAll()).Returns(new Foo[]
                {
                    new Foo(){Id = 1, Name = "Fake Foo 1"}, 
                    new Foo(){Id = 2, Name = "Fake Foo 2"}, 
                    new Foo(){Id = 3, Name = "Fake Foo 3"}, 
                    new Foo(){Id = 4, Name = "Fake Foo 4"}
                }.AsQueryable());
    
                mock.Setup(m =>
                m.GetSingle(It.Is<int>(i =>i == 1 || i == 2 || i == 3 || i == 4))).Returns<int>(r => new Foo
                {
                    Id = r,
                    Name = string.Format("Fake Foo {0}", r)
                });
    
                fooRepository = mock.Object;
            }

    →测试返回类型

    [TestMethod]
            public void is_index_return_model_type_of_iqueryable_foo()
            {
                //Arragne
                FooController fooController = new FooController(fooRepository);
    
                //Act
                var indexModel = fooController.Index().Model;
    
                //Assert
                Assert.IsInstanceOfType(indexModel, typeof(IQueryable<Foo>));
            }
    
            [TestMethod]
            public void is_details_returns_type_of_ViewResult()
            {
                //Arrange
                FooController fooController = new FooController(fooRepository);
    
                //Act
                var detailsResult = fooController.Details(1);
    
                //Assert
                Assert.IsInstanceOfType(detailsResult, typeof(ViewResult));
            }
    
            [TestMethod]
            public void is_details_returns_type_of_HttpNotFoundResult()
            {
                //Arrange
                FooController fooController = new FooController(fooRepository);
    
                //Act
                var detailsResult = fooController.Details(5);
    
                //Assert
                Assert.IsInstanceOfType(detailsResult, typeof(HttpNotFoundResult));
            }

    →测试返回集合类型Model的数量  

    2

    结果:

    1

    参考资料:               
    How to Work With Generic Repositories on ASP.NET MVC and Unit Testing Them By Mocking

  • 相关阅读:
    webpack的安装与配置
    npm初始化
    gitignore的配置
    git本地已有文件夹和远程仓库对应
    git 配置
    开发环境和开发工具
    git 码云使用教程
    递归
    LeetCode 392. 判断子序列
    MongoDB基本操作
  • 原文地址:https://www.cnblogs.com/darrenji/p/3810636.html
Copyright © 2011-2022 走看看