asp.net-mvc-3 – 以下列方式对ASP.NET MVC代码进行单元测试可能
我一直在研究
the NuGetGallery中单元测试的完成方式.我观察到,当测试控制器时,服务类被嘲笑.这对我来说很有意义,因为在测试控制器逻辑时,我不想担心下面的架构层.在使用这种方法一段时间之后,我注意到当我的服务类发生变化时,我经常在我的控制器测试中修复我的模拟.为了解决这个问题,没有咨询比我聪明的人,我开始编写这样的测试(别担心,我没有那么远):
public class PersonController : Controller { private readonly LESRepository _repository; public PersonController(LESRepository repository) { _repository = repository; } public ActionResult Index(int id) { var model = _repository.GetAll<Person>() .FirstOrDefault(x => x.Id == id); var viewModel = new VMPerson(model); return View(viewModel); } } public class PersonControllerTests { public void can_get_person() { var person = _helper.CreatePerson(username: "John"); var controller = new PersonController(_repository); controller.FakeOutContext(); var result = (ViewResult)controller.Index(person.Id); var model = (VMPerson)result.Model; Assert.IsTrue(model.Person.Username == "John"); } } 我想这将是集成测试,因为我使用的是真正的数据库(我更喜欢内存数据库).我通过将数据放入我的数据库开始我的测试(每个测试在一个事务中运行,并在测试完成时回滚).然后我调用我的控制器,我真的不在乎它如何从数据库中检索数据(通过存储库或服务类)只是要发送到视图的模型必须有我放入数据库的记录,也就是我的断言.关于这种方法的一个很酷的事情是,很多时候我可以继续添加更多层的复杂性,而无需更改我的控制器测试: public class PersonController : Controller { private readonly LESRepository _repository; private readonly PersonService _personService; public PersonController(LESRepository repository) { _repository = repository; _personService = new PersonService(_repository); } public ActionResult Index(int id) { var model = _personService.GetActivePerson(id); if(model == null) return PersonNotFoundResult(); var viewModel = new VMPerson(model); return View(viewModel); } } 现在我意识到我没有为PersonService创建一个接口并将其传递给我的控制器的构造函数.原因是1)我不打算模拟我的PersonService和2)我不觉得我需要注入我的依赖,因为我的PersonController现在只需要依赖一种类型的PersonService. 我是单位测试的新手,我总是很高兴被证明我错了.请指出为什么我测试我的控制器的方式可能是一个非常糟糕的主意(除了我的测试运行时间明显增加). 解决方法
嗯.这里有几件事情.
首先,看起来您正在尝试测试控制器方法.太棒了:) 所以这意味着,控制器需要的任何东西都应该被嘲笑.这是因为 >您不想担心该依赖项内发生的事情. 好吧,让我们来看看你做了什么,我会看看我是否可以重构它以使其更加可测试. -EMEMBER-我正在测试CONTROLLER METHOD,而不是控制器方法调用/依赖的东西. 所以这意味着我不关心服务实例或存储库实例(您决定遵循的架构方式). 注意:我保持简单,所以我已经删除了很多废话,等等. 接口 首先,我们需要一个存储库接口.这可以实现为内存中的repo,实体框架repo等.你很快就会明白为什么. public interface ILESRepository { IQueryable<Person> GetAll(); } 调节器 在这里,我们使用界面.这意味着使用模拟IRepository或真实实例非常简单和棒极了. public class PersonController : Controller { private readonly ILESRepository _repository; public PersonController(ILESRepository repository) { if (repository == null) { throw new ArgumentNullException("repository"); } _repository = repository; } public ActionResult Index(int id) { var model = _repository.GetAll<Person>() .FirstOrDefault(x => x.Id == id); var viewModel = new VMPerson(model); return View(viewModel); } } 单元测试 好的 – 这是神奇的金钱拍摄的东西. public static class FakePeople() { public static IList<Person> GetSomeFakePeople() { return new List<Person> { new Person { Id = 1,Name = "John" },new Person { Id = 2,Name = "Fred" },new Person { Id = 3,Name = "Sally" },} } } 现在我们进行了测试.我正在使用xUnit作为我的测试框架,并使用moq进行模拟.这里有任何框架都可以. public class PersonControllerTests { [Fact] public void GivenAListOfPeople_Index_Returns1Person() { // Arrange. var mockRepository = new Mock<ILESRepository>(); mockRepository.Setup(x => x.GetAll<Person>()) .Returns( FakePeople.GetSomeFakePeople() .AsQueryable); var controller = new PersonController(mockRepository); controller.FakeOutContext(); // Act. var result = controller.Index(person.Id) as ViewResult; // Assert. Assert.NotNull(result); var model = result.Model as VMPerson; Assert.NotNull(model); Assert.Equal(1,model.Person.Id); Assert.Equal("John",model.Person.Username); // Make sure we actually called the GetAll<Person>() method on our mock. mockRepository.Verify(x => x.GetAll<Person>(),Times.Once()); } } 好吧,让我们来看看我做了什么. 首先,我安排我的废话.我首先创建一个ILESRepository的模拟. 所以这就是控制器会发生的事情…… var model = _repository.GetAll<Person>() .FirstOrDefault(x => x.Id == id); 首先,我们要求mock使用GetAll< Person>()方法.我只是’设置’来返回一个人的列表..所以我们有一个3 Person对象的列表.接下来,我们在这个3 Person对象列表上调用FirstOrDefault(…)..它返回单个对象或null,具体取决于id的值. 田田!这是钱拍:) 现在回到单元测试的其余部分. 我们行动然后我们断言.那里没什么难的. 有时,您可能希望检查错误的情况,例如传递错误数据的人.这意味着您可能永远不会得到模拟方法(这是正确的),因此您验证它们从未被调用过. 好的 – 问题,课程? (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- asp.net – 会话在20分钟后到期:是否空闲
- asp.net-mvc – 文件“?/ Views/Position/Edit.cshtml”不能
- asp.net – 在乌龟SVN操作后丢失的IIS_IUSRS权限
- asp.net-mvc – 如何使用AJAX调用更新MVC中带有Add和Delete
- asp.net-mvc – 将冗余信息添加到MVC路由
- ASP.NET SQL成员资格表
- asp.net – 构建时验证所有ASPX,ASCX和HTML文件
- asp.net-mvc-3 – 在WebGrid中向Header添加自定义html
- 【ASP.NET Core】AddMvc和AddMvcCore的区别
- asp.net – 用于Active Directory帐户的Oauth 2令牌
- 如何将TextBox双向绑定到ASP.NET中的代码隐藏属性
- asp.net-mvc – 将ASP.NET MVC应用程序部署到IIS
- oauth-2.0 – AspNet身份2:自定义OAuth端点响应
- asp.net – 如何在单击“赞”按钮时在新选项卡中
- asp.net-mvc – MVC3,Ninject,MvcSiteMapProvide
- 从旧的经典ASP页面设置301重定向到新的ASP.NET W
- asp.net-mvc – SimpleMembershipProvider不会在
- asp.net-mvc – 类库中的控制器和视图
- asp.net – 如何在网格视图列标题上添加鼠标悬停
- asp.net-mvc – 如何将ms显示从LHS移动到RHS