《ASP.NET MVC 4 实战》学习笔记 5:控制器(下)

三、单元测试--确保控制器按照希望执行:

单元测试是小型的脚本化测试,通常以与产品代码同样的语言来编写。它们以与系统其余部分隔离的形式来建立并演练单个组件的功能,目的是证实它能正确工作。

1.已提供的测试项目:

如果在新建项目时勾选了“创建单元测试项目”选项,Visual Studio会用Visual Studio Unit Testing Framework(Visual Studio单元测试框架)生成一个测试项目,包含HomeControllerTest类:

 1 namespace Guestbook.Tests.Controllers
 2 {
 3     [TestClass]
 4     public class HomeControllerTest
 5     {
 6         [TestMethod]
 7         public void Index()
 8         {
 9             // Arrange
10             HomeController controller = new HomeController();//实例化控制器
11 
12             // Act
13             ViewResult result = controller.Index() as ViewResult;//演练动作方法
14 
15             // Assert
16             Assert.IsNotNull(result);//断言结果
17         }
18 
19         [TestMethod]
20         public void About()
21         {
22             // Arrange
23             HomeController controller = new HomeController();
24 
25             // Act
26             ViewResult result = controller.About() as ViewResult;
27 
28             // Assert
29             Assert.AreEqual("Your application description page.", result.ViewBag.Message);
30         }
31 
32         [TestMethod]
33         public void Contact()
34         {
35             // Arrange
36             HomeController controller = new HomeController();
37 
38             // Act
39             ViewResult result = controller.Contact() as ViewResult;
40 
41             // Assert
42             Assert.IsNotNull(result);
43         }
44     }

 每个测试都有三个阶段--Arrange(准备)、Act(动作)、Assert(断言)。通常将这种编写测试的模式称为“A/A/A”模式或“3A”模式。让单元测试采用这种固定的A/A/A编程模式有利于编写格式统一的测试。

2.测试GuestbookController:
在之前的代码中GuestbookController直接实例化并使用GuestbookContext对象访问数据库。这意味着如果没有建好数据库或者数据库中没有测试数据,就不能对控制器进行测试。

代替上述对GuestbookContext的直接访问,我们引入一个存储库,让它提供一个对GuestbookEntry对象执行数据访问操作的网关:

1.新增接口:

 1 namespace Guestbook.Repository
 2 {
 3     public interface IGuestbookRepository
 4     {
 5         IList<GuestbookEntry> GetMostRecentEntries();
 6         GuestbookEntry FindById(int? id);
 7         IList<CommentSummary> GetCommentSummary();
 8         void AddEntry(GuestbookEntry entry);
 9         void Edit(GuestbookEntry entry);
10         void DeleteById(int id);
11     }
12 }

 2.实现接口:

 1 namespace Guestbook.Repository
 2 {
 3     public class GuestbookRepository : IGuestbookRepository
 4     {
 5         private GuestbookContext _db = new GuestbookContext();
 6         public IList<GuestbookEntry> GetMostRecentEntries()
 7         {
 8             return (from entry in _db.Entries
 9                     orderby entry.DateAdded descending
10                     select entry).Take(20).ToList();
11         }
12         public void AddEntry(GuestbookEntry entry)
13         {
14             _db.Entries.Add(entry);
15             _db.SaveChanges(); 
16         }
17         public GuestbookEntry FindById(int? id)
18         {
19             var entry = _db.Entries.Find(id);
20             return entry;
21         }
22         public IList<CommentSummary> GetCommentSummary()
23         {
24             var entries = from entry in _db.Entries
25                           group entry by entry.Name into groupedByName
26                           orderby groupedByName.Count() descending
27                           select new CommentSummary 
28                           {
29                               NumberOfComments=groupedByName.Count(),
30                               UserName=groupedByName.Key
31                           };
32             return entries.ToList();
33 
34         }
35         public void Edit(GuestbookEntry entry)
36         {
37             _db.Entry(entry).State = EntityState.Modified;
38             _db.SaveChanges();
39         }
40         public void DeleteById(int id)
41         {
42             var entry = _db.Entries.Find(id);
43             _db.Entries.Remove(entry);
44             _db.SaveChanges();
45         }
46     }
47 }
View Code

3.修改GuestbookController:

  1 namespace Guestbook.Controllers
  2 {
  3     public class GuestbookController : Controller
  4     {
  5         private IGuestbookRepository _repository;
  6 
  7         public GuestbookController()
  8         {
  9             _repository = new GuestbookRepository();
 10         }
 11 
 12         public GuestbookController(IGuestbookRepository repository)
 13         {
 14             _repository = repository;
 15         }
 16         public ActionResult Index()
 17         {
 18             var mostRecentEntries=_repository.GetMostRecentEntries();
 19             return View(mostRecentEntries);
 20         }
 21         public ActionResult Details(int? id)
 22         {
 23             if (id == null)
 24             {
 25                 return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
 26             }
 27             var entry = _repository.FindById(id);
 28             if (entry == null)
 29             {
 30                 return HttpNotFound();
 31             }
 32             return View(entry);
 33         }
 34         public ActionResult Create()
 35         {
 36             return View();
 37         }
 38         [HttpPost]
 39         [ValidateAntiForgeryToken]
 40         public ActionResult Create([Bind(Include="Id,Name,Message,DateAdded")] GuestbookEntry entry)
 41         {
 42             if (ModelState.IsValid)//检查验证是否成功
 43             {
 44                 _repository.AddEntry(entry);
 45                 return RedirectToAction("Index");
 46             }
 47 
 48             return View(entry);//失败时重新渲染表单
 49         }
 50         public ActionResult Edit(int? id)
 51         {
 52             if (id == null)
 53             {
 54                 return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
 55             }
 56             var entry = _repository.FindById(id);
 57             if (entry == null)
 58             {
 59                 return HttpNotFound();
 60             }
 61             return View(entry);
 62         }
 63         [HttpPost]
 64         [ValidateAntiForgeryToken]
 65         public ActionResult Edit([Bind(Include="Id,Name,Message,DateAdded")] GuestbookEntry entry)
 66         {
 67             if (ModelState.IsValid)
 68             {
 69                 _repository.Edit(entry);
 70                 return RedirectToAction("Index");
 71             }
 72             return View(entry);
 73         }
 74         public ActionResult Delete(int? id)
 75         {
 76             if (id == null)
 77             {
 78                 return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
 79             }
 80             var entry = _repository.FindById(id);
 81             if (entry == null)
 82             {
 83                 return HttpNotFound();
 84             }
 85             return View(entry);
 86         }
 87         [HttpPost, ActionName("Delete")]
 88         [ValidateAntiForgeryToken]
 89         public ActionResult DeleteConfirmed(int id)
 90         {
 91             _repository.DeleteById(id);
 92             return RedirectToAction("Index");
 93         }
 94 
 95         public ActionResult CommentSumary()
 96         {
 97             var entries = _repository.GetCommentSummary();
 98             return View(entries.ToList());
 99         }
100     }
101 }
View Code

4.不与数据库交互的IGuestbookRepository接口的模仿实现:

 1 namespace Guestbook.Repository
 2 {
 3     public class FakeGuestbookRepository:IGuestbookRepository
 4     {
 5         public List<GuestbookEntry> _entries = new List<GuestbookEntry>();
 6         public IList<GuestbookEntry> GetMostRecentEntries()
 7         {
 8             return new List<GuestbookEntry> 
 9             {
10                 new GuestbookEntry
11                 {
12                   DateAdded=new DateTime(2014,11,13),
13                   Id=1,
14                   Message="Test Message",
15                   Name="陈家洛"
16                 }
17             };
18         }
19         public void AddEntry(GuestbookEntry entry)
20         {
21             _entries.Add(entry);
22         }
23         public GuestbookEntry FindById(int? id)
24         {
25             return _entries.SingleOrDefault(x => x.Id == id);
26         }
27         public IList<CommentSummary> GetCommentSummary()
28         {
29             return new List<CommentSummary> 
30             {
31                 new CommentSummary
32                 {
33                     UserName="陈家洛",
34                     NumberOfComments=3
35                 }
36             };
37 
38         }
39         public void Edit(GuestbookEntry entry)
40         {
41             //
42         }
43         public void DeleteById(int id)
44         {
45             //
46         }
47     }
48 }
View Code

 5.测试Index动作:

 1 namespace Guestbook.Tests.Controllers
 2 {
 3     [TestClass]
 4     public class GuestbookControllerTest
 5     {
 6         [TestMethod]
 7         public void Index_RendersView()
 8         {
 9             var controller = new GuestbookController(new FakeGuestbookRepository());
10             var result=controller.Index() as ViewResult;
11             Assert.IsNotNull(result);
12         }
13         [TestMethod]
14         public void Index_gets_most_rencent_entries()
15         {
16             var controller = new GuestbookController(new FakeGuestbookRepository());
17             var result=(ViewResult)controller.Index();
18             var entries=(IList<GuestbookEntry>) result.Model;
19             Assert.AreEqual(1,entries.Count);
20         }
21 
22     }
23 }

第一个测试调用Index动作,并简单断言它渲染一个视图;第二个断言传递给视图一个GuestbookEntry对象列表。

源码下载 密码:bwmq

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。