2023-06-15 06:16:35 来源:博客园
前言
其实就是官方的例子,只是在此收录整理一下。
正文
测试控制器测试的是什么呢?
测试的是避开筛选器、路由、模型绑定,就是只测试控制器的逻辑,但是不测试器依赖项。
【资料图】
代码部分:
https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/mvc/controllers/testing/samples/第一个例子:
[Fact]public async Task Index_ReturnsAViewResult_WithAListOfBrainstormSessions(){// arrangevar mockRepo = new Mock();mockRepo.Setup(repo => repo.ListAsync()).ReturnsAsync(GetTestSessions());var controller = new HomeController(mockRepo.Object);// actvar result = await controller.Index();// assertvar viewResult = Assert.IsType(result);var model = Assert.IsAssignableFrom>(viewResult.ViewData.Model);Assert.Equal(2, model.Count());}private List GetTestSessions(){var sessions = new List();sessions.Add(new BrainstormSession(){DateCreated = new DateTime(2016, 7, 2),Id = 1,Name = "Test One"});sessions.Add(new BrainstormSession(){DateCreated = new DateTime(2016, 7, 1),Id = 2,Name = "Test Two"});return sessions;} Index_ReturnsAViewResult_WithAListOfBrainstormSessions 命名规则:
Index 是测试的方法。
ReturnsAViewResult 是结果
WithAListOfBrainstormSessions 是条件
var viewResult = Assert.IsType(result);var model = Assert.IsAssignableFrom>(viewResult.ViewData.Model);Assert.Equal(2, model.Count()); 有3个测试的地方:
测试结果类型是ViewResult
测试viewResult.ViewData.Model,是IEnumerable
的派生 测试model的数量为2
第二个例子:
[Fact]public async Task IndexPost_RetrunsBadRequestResult_WhenModelStateInvalid(){// Arrangevar mockRepo = new Mock();mockRepo.Setup(repo => repo.ListAsync()).ReturnsAsync(GetTestSessions());var controller = new HomeController(mockRepo.Object);controller.ModelState.AddModelError("SessionName", "Required");var newSession = new HomeController.NewSessionModel();// Actvar result = await controller.Index(newSession);// Assertvar badRequestResult = Assert.IsType(result);Assert.IsType(badRequestResult.Value);} 进行ModelState 验证, 因为没有走mvc,那么需要自己设置验证信息。
[Fact]public async Task IndexPost_ReturnsARedirectAndAddsSession_WhenModelStateIsValid(){// Arrangevar mockRepo = new Mock();mockRepo.Setup(repo => repo.AddAsync(It.IsAny())).Returns(Task.CompletedTask).Verifiable();var controller = new HomeController(mockRepo.Object);var newSession = new HomeController.NewSessionModel(){SessionName = "Test Name"};// Actvar result = await controller.Index(newSession);// Assertvar redirectToActionResult = Assert.IsType(result);Assert.Null(redirectToActionResult.ControllerName);Assert.Equal("Index", redirectToActionResult.ActionName);mockRepo.Verify();} 这里主要介绍的是mockRepo.Verify(),在mockRepo 模拟方法的时候AddAsync设置,传入的类型要是BrainstormSession。
如果不是的话,那么就会抛出异常。
it 还有很多其他的选项,比如不能为空等。
然后为什么这里是两个测试呢? 这里要介绍的是单元测试,必须要覆盖测试,分布测试不同情况。
例子3:
那么下面测试SessionController:
这里开始有些变化了,注意观察:
[Fact]public async Task IndexReturnsARedirectToIndexHomeWhenIdIsNull(){// Arrangevar controller = new SessionController(sessionRepository: null);// Actvar result = await controller.Index(id: null);// Assertvar redirectToActionResult =Assert.IsType(result);Assert.Equal("Home", redirectToActionResult.ControllerName);Assert.Equal("Index", redirectToActionResult.ActionName);}[Fact]public async Task IndexReturnsContentWithSessionNotFoundWhenSessionNotFound(){// Arrangeint testSessionId = 1;var mockRepo = new Mock();mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId)).ReturnsAsync((BrainstormSession)null);var controller = new SessionController(mockRepo.Object);// Actvar result = await controller.Index(testSessionId);// Assertvar contentResult = Assert.IsType(result);Assert.Equal("Session not found.", contentResult.Content);}public async Task IndexReturnsViewResultWithStormSessionViewModel(){var testSessionId = 1;var mockRepo = new Mock();mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId)).ReturnsAsync(GetTestSessions().FirstOrDefault(s => s.Id == testSessionId));var controller = new SessionController(mockRepo.Object);// actvar result = controller.Index(testSessionId);// Assertvar viewResult = Assert.IsType(result);var model = Assert.IsType(viewResult.ViewData.Model);Assert.Equal("Test One", model.Name);Assert.Equal(2, model.DateCreated.Day);Assert.Equal(testSessionId, model.Id);}private List GetTestSessions(){var sessions = new List();sessions.Add(new BrainstormSession(){DateCreated = new DateTime(2016, 7, 2),Id = 1,Name = "Test One"});sessions.Add(new BrainstormSession(){DateCreated = new DateTime(2016, 7, 1),Id = 2,Name = "Test Two"});return sessions;} 上面不仅将方法的各种情况都覆盖了,还注意到命名:
以前命名是测试方式_测试结果_测试条件。
现在命名是一个句子来描述了,这样做的目的是使得写代码的人读起来更加通顺。
// Arrangeint testSessionId = 1;var mockRepo = new Mock();mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId)).ReturnsAsync(GetTestSessions().FirstOrDefault(s => s.Id == testSessionId)); 这个点值得关注一下, 比如说去模拟GetByIdAsync的返回信息,是可以进行自我实现的,而不是单纯的传递一个值。
前面的例子验证了查询这块,那么第四个例子,来验证创建这块。
[Fact]public async Task Create_ReturnsBadRequest_GivenInvalidModel(){ // Arrange & Act var mockRepo = new Mock(); var controller = new IdeasController(mockRepo.Object); controller.ModelState.AddModelError("error", "some error"); // Act var result = await controller.Create(model: null); // Assert Assert.IsType(result);} [Fact]public async Task Create_ReturnsHttpNotFound_ForInvalidSession(){ // Arrange int testSessionId = 123; var mockRepo = new Mock(); mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId)) .ReturnsAsync((BrainstormSession)null); var controller = new IdeasController(mockRepo.Object); // Act var result = await controller.Create(new NewIdeaModel()); // Assert Assert.IsType(result);} [Fact]public async Task Create_ReturnsNewlyCreatedIdeaForSession(){// Arrangeint testSessionId = 1;string testName = "test name";string testDescription = "test description";var testSession = GetTestSession();var mockRepo = new Mock();mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId)).ReturnsAsync(testSession);var controller = new IdeasController(mockRepo.Object);var newIdea = new NewIdeaModel(){Description = testDescription,Name = testName,SessionId = testSessionId};mockRepo.Setup(repo => repo.UpdateAsync(testSession)).Returns(Task.CompletedTask).Verifiable();// Actvar result = await controller.Create(newIdea);// Assertvar okResult = Assert.IsType(result);var returnSession = Assert.IsType(okResult.Value);mockRepo.Verify();Assert.Equal(1, returnSession.Ideas.Count());Assert.Equal(testName, returnSession.Ideas.LastOrDefault().Name);Assert.Equal(testDescription, returnSession.Ideas.LastOrDefault().Description);}private BrainstormSession GetTestSession(){return new BrainstormSession(){DateCreated = new DateTime(2016, 7, 1),Id = 2,Name = "Test Two",};} 值得注意的是最后这个:
UpdateAsync 这个并不需要我们过多的逻辑去测试,这个是单元测试,而不需要关注依赖的细节。
然后这个有个Verifiable,这个是验证什么的呢? 验证UpdateAsync 传入的对象是testSession,如果不是的话,那么:
[Fact]public async Task Create_ReturnsNewlyCreatedIdeaForSession(){// Arrangeint testSessionId = 1;string testName = "test name";string testDescription = "test description";var testSession = GetTestSession();var mockRepo = new Mock();mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId)).ReturnsAsync(testSession);var controller = new IdeasController(mockRepo.Object);var newIdea = new NewIdeaModel(){Description = testDescription,Name = testName,SessionId = testSessionId};mockRepo.Setup(repo => repo.UpdateAsync(GetTestSession())).Returns(Task.CompletedTask).Verifiable();// Actvar result = await controller.Create(newIdea);// Assertvar okResult = Assert.IsType(result);var returnSession = Assert.IsType(okResult.Value);mockRepo.Verify();Assert.Equal(1, returnSession.Ideas.Count());Assert.Equal(testName, returnSession.Ideas.LastOrDefault().Name);Assert.Equal(testDescription, returnSession.Ideas.LastOrDefault().Description);} 比如我这样写,那么会报错:
那么其实记住的是,单元测试验证的输入和输出。
比如这里的xuit, 对于验证输出是很好验证的,那么为什么使用moq这个东西呢,理由也很简单哈,那就是验证传参,也就是验证输入。
这里还有另外一个问题,当要开始Assert时候,我们应该一个怎么样的顺序去验证呢?
一个基本的思路就是:
- 先验证输出的类型
var actionResult = Assert.IsType>(result);var createdAtActionResult = Assert.IsType(actionResult.Result);var returnValue = Assert.IsType(createdAtActionResult.Value); - 然后验证输入的参数
mockRepo.Verify();- 最后验证细节
Assert.Equal(2, returnValue.Ideas.Count());Assert.Equal(testName, returnValue.Ideas.LastOrDefault().Name);Assert.Equal(testDescription, returnValue.Ideas.LastOrDefault().Description);结
上面就是验证单元控制器的测试的基本思路了,其实的方法验证的差不多。
下一节集成测试。
标签:
- 加快虚拟仿真实训基地建设 启动职业学校信息化建设试点很必要
- “双减”后如何在满足学生多样需求方面做“加法”?
- 处于生理活跃期且心理发展不成熟 高校开设公共卫生必修课很必要
- 价格低于相应蔬菜零售价 西安投放约1万吨政府储备蔬菜
- 深受年轻消费群体所青睐 国潮风商品成为年货新选择
知识
- 他把银行卡卖给骗子,“黑吃黑”“截胡”十万元
- “老司机”4S店试驾豪车 结果油门当刹车撞了
- 新开工改造城镇老旧小区5.34万个
- 发动巡河志愿者2万余名 “用心护好每一条河”
- 假客服的套路:伪装成大平台客服,层层布局引人上钩
人物
- 浙江两轮核酸检测结果均为阴性 无新增本土阳性感染者
- 新疆阿克苏地区库车市发生4.1级地震 震源深度18千米
- 抵返哈尔滨人员须持48小时内核酸检测阴性证明
- 浙大紫金港校区已解封 有7337人有序离开该校区
- 2021年广东省第七届风筝锦标赛落幕
- 黑龙江讷河市启动全员核酸检测 目前讷河市全员核酸检测结果均为阴性
- 【同心粤港澳 携手大湾区】南头古城,搭建深港澳三地文化创意活动交流平台
- 重庆入河排污口整治工作推进至全市26个区县
- 四川省第二批政法队伍教育整顿:立案审查调查省级政法机关干警58人
- 长三角区域生态环境部门“云签约”长江大保护倡议书
- 古老长城重焕新生机
- 藏不住了!你同事里有许多“武林高手”……
- 浙江杭州2例无症状感染者系感染德尔塔变异株
- 喜马拉雅的深情和誓言
- 浪漫之城打造山海城一体新地标
- 让老年人更适应数字生活
- 内蒙古通辽市新增1例本土确诊病例、1例无症状感染者
- 徐州无新增确诊病例 核酸检测55515人结果均为阴性
- 甘肃培树“农家巧娘”增技能:返乡创业掌勺又“掌柜”
- 内蒙古通辽市科尔沁区一地调整为中风险地区
- 上海本轮疫情涉及闭环管理的医疗机构全面恢复门急诊
- 青年学生成艾滋病感染高发人群 “社会疫苗”如何打?
- 内蒙古满洲里新增本土确诊病例1例 当地开展第二轮大规模核酸检测
- 江西无新增本土确诊病例 上饶全面恢复正常生产生活秩序
- 中老铁路上会四国语言的列车长:用心维护中老友谊的桥梁
- 海南首次发现有环志的世界极危鸟种勺嘴鹬
- 一场“网络劝生者”和“网络劝死者”的战役
- 内蒙古通辽新增本土确诊和无症状感染者各1例 轨迹公布
- 江西中烟工业有限责任公司原总经理姚庆艳接受审查调查
- 宁夏45例新冠肺炎确诊病例均已治愈出院
- 内蒙古通辽市科尔沁区发现2名初筛阳性人员
- 生活在闹钟里的丈夫:自己迟一秒,渐冻症妻子就会多一分疼
- 辽宁新冠肺炎确诊病例零新增
- 11月28日16-24时,内蒙古新增本土确诊病例1例
- 奥密克戎毒株为何“需要关注”?现有防疫工具还有效吗?
- 黑龙江新增本土无症状感染者1例
- 这辈子一定要去趟这个公园 在这里“有种爱叫放手”
- 那年今日 | 一张漫画涨知识之11月29日
- 寒潮预警!我国中东部迎大范围降温 黑龙江等地降幅可达12℃
- 冷空气继续影响我国中东部 华北黄淮等地有雾和霾天气
