最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

焦点速讯:重新整理 .net core 实践篇——— 测试控制器[四十九]

来源:博客园

前言

其实就是官方的例子,只是在此收录整理一下。

正文

测试控制器测试的是什么呢?


(资料图)

测试的是避开筛选器、路由、模型绑定,就是只测试控制器的逻辑,但是不测试器依赖项。

代码部分:

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个测试的地方:

  1. 测试结果类型是ViewResult

  2. 测试viewResult.ViewData.Model,是IEnumerable的派生

  3. 测试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时候,我们应该一个怎么样的顺序去验证呢?

一个基本的思路就是:

  1. 先验证输出的类型
var actionResult = Assert.IsType>(result);var createdAtActionResult = Assert.IsType(actionResult.Result);var returnValue = Assert.IsType(createdAtActionResult.Value);
  1. 然后验证输入的参数
mockRepo.Verify();
  1. 最后验证细节
Assert.Equal(2, returnValue.Ideas.Count());Assert.Equal(testName, returnValue.Ideas.LastOrDefault().Name);Assert.Equal(testDescription, returnValue.Ideas.LastOrDefault().Description);

上面就是验证单元控制器的测试的基本思路了,其实的方法验证的差不多。

下一节集成测试。

关键词: