一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語(yǔ)言|JavaScript|易語(yǔ)言|vb.net|

服務(wù)器之家 - 編程語(yǔ)言 - ASP.NET教程 - ASP.NET Core對(duì)Controller進(jìn)行單元測(cè)試的完整步驟

ASP.NET Core對(duì)Controller進(jìn)行單元測(cè)試的完整步驟

2020-09-14 16:52Agile.Zhou ASP.NET教程

這篇文章主要給大家介紹了關(guān)于ASP.NET Core對(duì)Controller進(jìn)行單元測(cè)試的完整步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用ASP.NET Core具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧

前言

單元測(cè)試對(duì)我們的代碼質(zhì)量非常重要。很多同學(xué)都會(huì)對(duì)業(yè)務(wù)邏輯或者工具方法寫測(cè)試用例,但是往往忽略了對(duì)Controller層寫單元測(cè)試。我所在的公司沒見過一個(gè)對(duì)Controller寫過測(cè)試的。今天來演示下如果對(duì)Controller進(jìn)行單元測(cè)試。以下內(nèi)容默認(rèn)您對(duì)單元測(cè)試有所了解,比如如何mock一個(gè)接口。在這里多叨叨一句,面向接口的好處,除了能夠快速的替換實(shí)現(xiàn)類(其實(shí)大部分接口不會(huì)有多個(gè)實(shí)現(xiàn)),最大的好處就是可以進(jìn)行mock,可以進(jìn)行單元測(cè)試。

測(cè)試Action

下面的Action非常簡(jiǎn)單,非常常見的一種代碼。根據(jù)用戶id去獲取用戶信息然后展示出來。下面看看如何對(duì)這個(gè)Action進(jìn)行測(cè)試。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class UserController : Controller
{
  private readonly IUserService _userService;
  public UserController(IUserService userService)
  {
    _userService = userService;
  }
 
  public IActionResult UserInfo(string userId)
  {
    if (string.IsNullOrEmpty(userId))
    {
      throw new ArgumentNullException(nameof(userId));
    }
 
    var user = _userService.Get(userId);
    return View(user);
  }
 
}

測(cè)試代碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[TestMethod()]
   public void UserInfoTest()
   {
 
     var userService = new Mock<IUserService>();
     userService.Setup(s => s.Get(It.IsAny<string>())).Returns(new User());
 
     var ctrl = new UserController(userService.Object);
     //對(duì)空參數(shù)進(jìn)行assert
     Assert.ThrowsException<ArgumentNullException>(() => {
       var result = ctrl.UserInfo(null);
     });
     //對(duì)空參數(shù)進(jìn)行assert
     Assert.ThrowsException<ArgumentNullException>(() => {
       var result = ctrl.UserInfo("");
     });
 
     var result = ctrl.UserInfo("1");
     Assert.IsNotNull(result);
     Assert.IsInstanceOfType(result, typeof(ViewResult));
   }

我們對(duì)一個(gè)Action進(jìn)行測(cè)試主要的思路就是模擬各種入?yún)ⅲ箿y(cè)試代碼能夠到達(dá)所有的分支,并且Assert輸出是否為空,是否為指定的類型等。

對(duì)ViewModel進(jìn)行測(cè)試

我們編寫Action的時(shí)候還會(huì)涉及ViewModel給視圖傳遞數(shù)據(jù),這部分也需要進(jìn)行測(cè)試。修改測(cè)試用例,加入對(duì)ViewModel的測(cè)試代碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
[TestMethod()]
   public void UserInfoTest()
   {
     var userService = new Mock<IUserService>();
     userService.Setup(s => s.Get(It.IsAny<string>())).Returns(new User()
     {
       Id = "x"
     }) ;
 
     var ctrl = new UserController(userService.Object);
     Assert.ThrowsException<ArgumentNullException>(() => {
       var result = ctrl.UserInfo(null);
     });
     Assert.ThrowsException<ArgumentNullException>(() => {
       var result = ctrl.UserInfo("");
     });
 
     var result = ctrl.UserInfo("1");
     Assert.IsNotNull(result);
     Assert.IsInstanceOfType(result, typeof(ViewResult));
     //對(duì)viewModel進(jìn)行assert
     var vr = result as ViewResult;
     Assert.IsNotNull(vr.Model);
     Assert.IsInstanceOfType(vr.Model, typeof(User));
     var user = vr.Model as User;
     Assert.AreEqual("x", user.Id);
   }

對(duì)ViewData進(jìn)行測(cè)試

我們編寫Action的時(shí)候還會(huì)涉及ViewData給視圖傳遞數(shù)據(jù),這部分同樣需要測(cè)試。修改Action代碼,對(duì)ViewData進(jìn)行賦值:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
public IActionResult UserInfo(string userId)
  {
    if (string.IsNullOrEmpty(userId))
    {
      throw new ArgumentNullException(nameof(userId));
    }
 
    var user = _userService.Get(userId);
 
    ViewData["title"] = "user_info";
 
    return View(user);
  }

修改測(cè)試用例,加入對(duì)ViewData的測(cè)試代碼:

  1. [TestMethod()] 
  2.   public void UserInfoTest() 
  3.   { 
  4.     var userService = new Mock<IUserService>(); 
  5.     userService.Setup(s => s.Get(It.IsAny<string>())).Returns(new User() 
  6.     { 
  7.       Id = "x" 
  8.     }) ; 
  9.  
  10.     var ctrl = new UserController(userService.Object); 
  11.     Assert.ThrowsException<ArgumentNullException>(() => { 
  12.       var result = ctrl.UserInfo(null); 
  13.     }); 
  14.     Assert.ThrowsException<ArgumentNullException>(() => { 
  15.       var result = ctrl.UserInfo(""); 
  16.     }); 
  17.  
  18.     var result = ctrl.UserInfo("1"); 
  19.     Assert.IsNotNull(result); 
  20.     Assert.IsInstanceOfType(result, typeof(ViewResult)); 
  21.  
  22.     var vr = result as ViewResult; 
  23.     Assert.IsNotNull(vr.Model); 
  24.     Assert.IsInstanceOfType(vr.Model, typeof(User)); 
  25.     var user = vr.Model as User; 
  26.     Assert.AreEqual("x", user.Id); 
  27.     //對(duì)viewData進(jìn)行assert 
  28.     Assert.IsTrue(vr.ViewData.ContainsKey("title")); 
  29.     var title = vr.ViewData["title"]; 
  30.     Assert.AreEqual("user_info", title); 
  31.   } 

對(duì)ViewBag進(jìn)行測(cè)試

因?yàn)閂iewBag事實(shí)上是ViewData的dynamic類型的包裝,所以Action代碼不用改,可以直接對(duì)ViewBag進(jìn)行測(cè)試:

  1. [TestMethod()] 
  2.  public void UserInfoTest() 
  3.  { 
  4.    var userService = new Mock<IUserService>(); 
  5.    userService.Setup(s => s.Get(It.IsAny<string>())).Returns(new User() 
  6.    { 
  7.      Id = "x" 
  8.    }) ; 
  9.  
  10.    var ctrl = new UserController(userService.Object); 
  11.    Assert.ThrowsException<ArgumentNullException>(() => { 
  12.      var result = ctrl.UserInfo(null); 
  13.    }); 
  14.    Assert.ThrowsException<ArgumentNullException>(() => { 
  15.      var result = ctrl.UserInfo(""); 
  16.    }); 
  17.  
  18.    var result = ctrl.UserInfo("1"); 
  19.    Assert.IsNotNull(result); 
  20.    Assert.IsInstanceOfType(result, typeof(ViewResult)); 
  21.  
  22.    var vr = result as ViewResult; 
  23.    Assert.IsNotNull(vr.Model); 
  24.    Assert.IsInstanceOfType(vr.Model, typeof(User)); 
  25.    var user = vr.Model as User; 
  26.    Assert.AreEqual("x", user.Id); 
  27.  
  28.    Assert.IsTrue(vr.ViewData.ContainsKey("title")); 
  29.    var title = vr.ViewData["title"]; 
  30.    Assert.AreEqual("user_info", title); 
  31.    //對(duì)viewBag進(jìn)行assert 
  32.    string title1 = ctrl.ViewBag.title; 
  33.    Assert.AreEqual("user_info", title1); 
  34.  } 

設(shè)置HttpContext

我們編寫Action的時(shí)候很多時(shí)候需要調(diào)用基類里的HttpContext,比如獲取Request對(duì)象,獲取Path,獲取Headers等等,所以有的時(shí)候需要自己實(shí)例化HttpContext以進(jìn)行測(cè)試。

?
1
2
3
var ctrl = new AccountController();
ctrl.ControllerContext = new ControllerContext();
ctrl.ControllerContext.HttpContext = new DefaultHttpContext();

對(duì)HttpContext.SignInAsync進(jìn)行mock

我們使用ASP.NET Core框架進(jìn)行登錄認(rèn)證的時(shí)候,往往使用HttpContext.SignInAsync進(jìn)行認(rèn)證授權(quán),所以單元測(cè)試的時(shí)候也需要進(jìn)行mock。下面是一個(gè)典型的登錄Action,對(duì)密碼進(jìn)行認(rèn)證后調(diào)用SignInAsync在客戶端生成登錄憑證,否則跳到登錄失敗頁(yè)面。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public async Task<IActionResult> Login(string password)
  {
    if (password == "123")
    {
      var claims = new List<Claim>
      {
       new Claim("UserName","x")
      };
      var authProperties = new AuthenticationProperties
      {
      };
      var claimsIdentity = new ClaimsIdentity(
       claims, CookieAuthenticationDefaults.AuthenticationScheme);
      await HttpContext.SignInAsync(
        CookieAuthenticationDefaults.AuthenticationScheme,
        new ClaimsPrincipal(claimsIdentity),
        authProperties);
      return Redirect("login_success");
    }
 
    return Redirect("login_fail");
  }

HttpContext.SignInAsync其實(shí)個(gè)時(shí)擴(kuò)展方法,SignInAsync其實(shí)最終是調(diào)用了IAuthenticationService里的SignInAsync方法。所以我們需要mock的就是IAuthenticationService接口,否者代碼走到HttpContext.SignInAsync會(huì)提示找不到IAuthenticationService的service。而IAuthenticationService本身是通過IServiceProvider注入到程序里的,所以同時(shí)需要mock接口IServiceProvider。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
[TestMethod()]
  public async Task LoginTest()
  {
    var ctrl = new AccountController();
 
    var authenticationService = new Mock<IAuthenticationService>();
    var sp = new Mock<IServiceProvider>();
    sp.Setup(s => s.GetService(typeof(IAuthenticationService)))
      .Returns(() => {
        return authenticationService.Object;
      });
    ctrl.ControllerContext = new ControllerContext();
    ctrl.ControllerContext.HttpContext = new DefaultHttpContext();
    ctrl.ControllerContext.HttpContext.RequestServices = sp.Object;
 
    var result = await ctrl.Login("123");
    Assert.IsNotNull(result);
    Assert.IsInstanceOfType(result, typeof(RedirectResult));
    var rr = result as RedirectResult;
    Assert.AreEqual("login_success", rr.Url);
 
    result = await ctrl.Login("1");
    Assert.IsNotNull(result);
    Assert.IsInstanceOfType(result, typeof(RedirectResult));
    rr = result as RedirectResult;
    Assert.AreEqual("login_fail", rr.Url);
  }

對(duì)HttpContext.AuthenticateAsync進(jìn)行mock

HttpContext.AuthenticateAsync同樣比較常用。這個(gè)擴(kuò)展方法同樣是在IAuthenticationService里,所以測(cè)試代碼跟上面的SignInAsync類似,只是需要對(duì)AuthenticateAsync繼續(xù)mock返回值success or fail。

?
1
2
3
4
5
6
7
8
9
public async Task<IActionResult> Login()
 {
   if ((await HttpContext.AuthenticateAsync()).Succeeded)
   {
     return Redirect("/home");
   }
 
   return Redirect("/login");
 }

測(cè)試用例:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
[TestMethod()]
public async Task LoginTest1()
{
  var authenticationService = new Mock<IAuthenticationService>();
  //設(shè)置AuthenticateAsync為success
  authenticationService.Setup(s => s.AuthenticateAsync(It.IsAny<HttpContext>(), It.IsAny<string>()))
    .ReturnsAsync(AuthenticateResult.Success(new AuthenticationTicket(new System.Security.Claims.ClaimsPrincipal(), "")));
  var sp = new Mock<IServiceProvider>();
  sp.Setup(s => s.GetService(typeof(IAuthenticationService)))
    .Returns(() => {
      return authenticationService.Object;
    });
 
  var ctrl = new AccountController();
  ctrl.ControllerContext = new ControllerContext();
  ctrl.ControllerContext.HttpContext = new DefaultHttpContext();
  ctrl.ControllerContext.HttpContext.RequestServices = sp.Object;
 
  var act = await ctrl.Login();
  Assert.IsNotNull(act);
  Assert.IsInstanceOfType(act, typeof(RedirectResult));
  var rd = act as RedirectResult;
  Assert.AreEqual("/home", rd.Url);
  //設(shè)置AuthenticateAsync為fail
  authenticationService.Setup(s => s.AuthenticateAsync(It.IsAny<HttpContext>(), It.IsAny<string>()))
    .ReturnsAsync(AuthenticateResult.Fail(""));
 
  act = await ctrl.Login();
  Assert.IsNotNull(act);
  Assert.IsInstanceOfType(act, typeof(RedirectResult));
  rd = act as RedirectResult;
  Assert.AreEqual("/login", rd.Url);
 
}

Filter進(jìn)行測(cè)試

我們寫Controller的時(shí)候往往需要配合很多Filter使用,所以Filter的測(cè)試也很重要。下面演示下如何對(duì)Fitler進(jìn)行測(cè)試。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MyFilter: ActionFilterAttribute
{
  public override void OnActionExecuting(ActionExecutingContext context)
  {
    if (context.HttpContext.Request.Path.Value.Contains("/abc/"))
    {
      context.Result = new ContentResult() {
        Content = "拒絕訪問"
      };
    }
 
    base.OnActionExecuting(context);
  }
}

對(duì)Filter的測(cè)試最主要的是模擬ActionExecutingContext參數(shù),以及其中的HttpContext等,然后對(duì)預(yù)期進(jìn)行Assert。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
[TestMethod()]
public void OnActionExecutingTest()
{
  var filter = new MyFilter();
  var actContext = new ActionContext(new DefaultHttpContext(),new RouteData(), new ActionDescriptor());
  actContext.HttpContext.Request.Path = "/abc/123";
  var listFilters = new List<IFilterMetadata>();
  var argDict = new Dictionary<string, object>();
  var actExContext = new ActionExecutingContext(
    actContext ,
    listFilters ,
    argDict ,
    new AccountController()
    );
   filter.OnActionExecuting(actExContext);
 
  Assert.IsNotNull(actExContext.Result);
  Assert.IsInstanceOfType(actExContext.Result, typeof(ContentResult));
  var cr = actExContext.Result as ContentResult;
  Assert.AreEqual("拒絕訪問", cr.Content);
 
  actContext = new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor());
  actContext.HttpContext.Request.Path = "/1/123";
  listFilters = new List<IFilterMetadata>();
  argDict = new Dictionary<string, object>();
  actExContext = new ActionExecutingContext(
    actContext,
    listFilters,
    argDict,
    new AccountController()
    );
  filter.OnActionExecuting(actExContext);
  Assert.IsNull(actExContext.Result);
}

總結(jié)

到此這篇關(guān)于ASP.NET Core對(duì)Controller進(jìn)行單元測(cè)試的文章就介紹到這了,更多相關(guān)ASP.NET Core對(duì)Controller單元測(cè)試內(nèi)容請(qǐng)搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!

原文鏈接:https://www.cnblogs.com/kklldog/p/unit-test-core-controller.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 亚洲国产天堂久久综合网站 | 9420高清完整版在线观看国语 | 成人亚洲欧美综合 | 美味情缘韩国在线观看视频 | free性日本 | 91制片厂(果冻传媒)原档破解 | 波多野结衣中文丝袜字幕 | 九九久久国产精品大片 | 九九国产在线视频 | 亚洲 日韩 国产 制服 在线 | 91午夜在线观看 | 草草剧场 | 51精品| 色综合图区 | 毛片影院 | 爱爱亚洲| 女仆色永久免费网站 | 日本亚欧乱色视频在线观看 | 久久机热视频 这里只有精品首页 | 国产精品麻豆久久99 | 手机在线观看国产精选免费 | 精品久久国产 | 国产在线三级 | 国产高清dvd | 久久91精品国产91久久户 | 久久三级网站 | 免费观看视频高清在线 | 国产裸舞福利资源在线视频 | 国内精品福利丝袜视频_速 国内精品91久久久久 | 农夫69小说恋老妇小说 | 丰满的闺蜜2中文字幕 | 亚洲欧美成人中文在线网站 | 男人最爱看的网站 | 亚洲国产精品久久网午夜小说 | 99任你躁精品视频 | 91制片厂免费观看 | 日韩毛片免费 | tobu8中国在线播放免费 | 经典欧美gifxxoo动态图暗网 | 久久精品热在线观看85 | 欧美三级做爰全过程 |