2016-10-27 6 views
3

私の単体テストに必要なHttpContextをシミュレートするのに苦労しています。ユニットテスト用のHttpContextを作成するにはどうすればよいですか?

SessionManagerインターフェイスを使用してMvcコントローラからセッションの制御を抽象化し、これをCookieSessionManagerというクラスで実装しました。 (初期開発段階)。

CookieSessionManagerは、注入されたシングルトンHttpContextAccessor(Startup.cs ConfigureServices内)を使用してHttpContextを使用します。

私は、app.UseCookieAuthenticationでStartup.csに設定されているCookie認証を使用しています。私が注入さMockSessionManagerクラスと私のAccountControllerクラスの仕事のために書かれている

MSUnitテストが期待通りにデバッグモードで

テストこのマニュアルでは動作します。

私の実際の問題は、私がCookieSessionManagerクラスのために書いたユニットテストです。私は以下のようにHttpContextをセットアップしようとしました。

[TestClass] 
public class CookieSessionManagerTest 
{ 
    private IHttpContextAccessor contextAccessor; 
    private HttpContext context; 
    private SessionManager sessionManager; 

    [TestInitialize] 
    public void Setup_CookieSessionManagerTest() 
    { 
     context = new DefaultHttpContext(); 

     contextAccessor = new HttpContextAccessor(); 

     contextAccessor.HttpContext = context; 

     sessionManager = new CookieSessionManager(contextAccessor); 
    } 

エラー

しかしsessionManager.Login(CreateValidApplicationUser());への呼び出しはIsAuthenticatedフラグとCookieSessionManager_Login_ValidUser_Authenticated_isTrueが失敗したテストを設定するためには表示されません。

[TestMethod] 
public void CookieSessionManager_Login_ValidUser_Authenticated_isTrue() 
{ 
    sessionManager.Login(CreateValidApplicationUser()); 

    Assert.IsTrue(sessionManager.isAuthenticated()); 
} 

public ApplicationUser CreateValidApplicationUser() 
{ 
    ApplicationUser applicationUser = new ApplicationUser(); 

    applicationUser.UserName = "ValidUser"; 

    //applicationUser.Password = "ValidPass"; 

    return applicationUser; 
} 

テスト名:CookieSessionManager_Login_ValidUser_Authenticated_isTrue

:ライン43試験結果:失敗した試験時間:0:00:00.0433169

結果のStackTrace:ClaimsWebAppTests.Identity.CookieSessionManagerTest.CookieSessionManager_Login_ValidUser_Authenticated_isTrueで()

CookieSessionManagerTest.cs:46行目結果メッセージ:Assert.IsTrueが失敗しました。

MY CODE

SessionManagerの

using ClaimsWebApp.Models; 

namespace ClaimsWebApp.Identity 
{ 
    public interface SessionManager 
    { 
     bool isAuthenticated(); 

     void Login(ApplicationUser applicationUser); 

     void Logout(); 
    } 
} 

CookieSessionManager

using ClaimsWebApp.Identity; 
using ClaimsWebApp.Models; 
using Microsoft.AspNetCore.Http; 
using System; 
using System.Collections.Generic; 
using System.Security.Claims; 

namespace ClaimsWebApp 
{ 
    public class CookieSessionManager : SessionManager 
    { 
     private List<ApplicationUser> applicationUsers; 
     private IHttpContextAccessor ContextAccessor; 
     private bool IsAuthenticated; 

     public CookieSessionManager(IHttpContextAccessor contextAccessor) 
     { 
      this.IsAuthenticated = false; 

      this.ContextAccessor = contextAccessor; 

      IsAuthenticated = ContextAccessor.HttpContext.User.Identity.IsAuthenticated; 

      applicationUsers = new List<ApplicationUser>(); 

      applicationUsers.Add(new ApplicationUser { UserName = "ValidUser" }); 
     } 
     public bool isAuthenticated() 
     { 
      return IsAuthenticated; 
     } 

     public void Login(ApplicationUser applicationUser) 
     { 
      if (applicationUsers.Find(m => m.UserName.Equals(applicationUser.UserName)) != null) 
      { 
       var identity = new ClaimsIdentity(new[] { 
       new Claim(ClaimTypes.Name, applicationUser.UserName) 
       }, 
       "MyCookieMiddlewareInstance"); 

       var principal = new ClaimsPrincipal(identity); 

       ContextAccessor.HttpContext.Authentication.SignInAsync("MyCookieMiddlewareInstance", principal); 

       IsAuthenticated = ContextAccessor.HttpContext.User.Identity.IsAuthenticated; 
      } 
      else 
      { 
       throw new Exception("User not found"); 
      } 
     } 

     public void Logout() 
     { 
      ContextAccessor.HttpContext.Authentication.SignOutAsync("MyCookieMiddlewareInstance"); 

      IsAuthenticated = ContextAccessor.HttpContext.User.Identity.IsAuthenticated; 
     } 
    } 
} 

スタート

using ClaimsWebApp.Identity; 
using Microsoft.AspNetCore.Builder; 
using Microsoft.AspNetCore.Hosting; 
using Microsoft.AspNetCore.Http; 
using Microsoft.Extensions.DependencyInjection; 
using Microsoft.Extensions.Logging; 

namespace ClaimsWebApp 
{ 
    public class Startup 
    { 
     // This method gets called by the runtime. Use this method to add services to the container. 
     // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940 
     public void ConfigureServices(IServiceCollection services) 
     { 
      services.AddMvc(); 
      services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); 
      services.AddScoped<SessionManager, CookieSessionManager>(); 
     } 

     // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 
     public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) 
     { 
      loggerFactory.AddConsole(); 

      app.UseCookieAuthentication(new CookieAuthenticationOptions() 
      { 
       AuthenticationScheme = "MyCookieMiddlewareInstance", 
       LoginPath = new PathString("/Account/Unauthorized/"), 
       AccessDeniedPath = new PathString("/Account/Forbidden/"), 
       AutomaticAuthenticate = true, 
       AutomaticChallenge = true 
      }); 

      app.UseMvc(routes => 
      { 
       routes.MapRoute(
        name: "default", 
        template: "{controller=Account}/{action=Login}/{id?}"); 
      }); 
     } 
    } 
} 

CookieSessionManagerTest up.cs。CS

using ClaimsWebApp; 
using ClaimsWebApp.Identity; 
using ClaimsWebApp.Models; 
using Microsoft.AspNetCore.Http; 
using Microsoft.VisualStudio.TestTools.UnitTesting; 

namespace ClaimsWebAppTests.Identity 
{ 
    [TestClass] 
    public class CookieSessionManagerTest 
    { 
     private IHttpContextAccessor contextAccessor; 
     private HttpContext context; 
     private SessionManager sessionManager; 

     [TestInitialize] 
     public void Setup_CookieSessionManagerTest() 
     { 
      context = new DefaultHttpContext(); 

      contextAccessor = new HttpContextAccessor(); 

      contextAccessor.HttpContext = context; 

      sessionManager = new CookieSessionManager(contextAccessor); 
     } 

     [TestMethod] 
     public void CookieSessionManager_Can_Be_Implemented() 
     { 
      Assert.IsInstanceOfType(sessionManager, typeof(SessionManager)); 
     } 


     [TestMethod] 
     public void CookieSessionManager_Default_Authenticated_isFalse() 
     { 
      Assert.IsFalse(sessionManager.isAuthenticated()); 
     } 

     [TestMethod] 
     public void CookieSessionManager_Login_ValidUser_Authenticated_isTrue() 
     { 
      sessionManager.Login(CreateValidApplicationUser()); 

      Assert.IsTrue(sessionManager.isAuthenticated()); 
     } 

     public ApplicationUser CreateValidApplicationUser() 
     { 
      ApplicationUser applicationUser = new ApplicationUser(); 

      applicationUser.UserName = "ValidUser"; 

      //applicationUser.Password = "ValidPass"; 

      return applicationUser; 
     } 

     public ApplicationUser CreateInValidApplicationUser() 
     { 
      ApplicationUser applicationUser = new ApplicationUser(); 

      applicationUser.UserName = "InValidUser"; 

      //applicationUser.Password = "ValidPass"; 

      return applicationUser; 
     } 
    } 
} 
+1

TestServerを使用して調査しましたか? https://docs.asp.net/en/latest/testing/integration-testing.html – Brad

+0

ありがとう@ブラッド、私はこれがまさに私が探しているものだと思います。私は実装の問題であるときに問題から単体テストを作るのが難しいです。 –

+1

TypeMockアイソレータでHttpContextをモックすることが可能です。例:https://www.typemock.com/docs?book=Isolator&page=Documentation%2FHtmlDocs%2Fsample1fakinghttpcontextandmodelstate.htm –

答えて

6

は残念ながら、それはHttpContextをテストするために、かなり不可能です。これはインターフェイスを使用しない密閉されたクラスなので、あなたはそれを嘲笑することはできません。通常、最も良い方法は、HttpContextで動作するコードを抽象化し、他のアプリケーション固有のコードだけをテストすることです。

これはすでにHttpContextAccessorで行われているようですが、間違って使用しています。まず、HttpContextのインスタンスを公開しています。これは、目的全体をかなり凌駕しています。このクラスは、httpContextAccessor.IsAuthenticatedのようにUser.Identity.IsAuthenticatedのようなものを単独で返すことができるはずです。内部的には、このプロパティはプライベートHttpContextインスタンスにアクセスし、結果を返します。

このようにしたら、HttpContextAccessorを試してテストに必要なものを返すだけで、HttpContextインスタンスを提供することを心配する必要はありません。

これはまだ未検証のコード、つまりHttpContextで動作するアクセサーメソッドがあることを意味しますが、これは一般的には非常に単純です。たとえば、IsAuthenticatedのコードは、return httpContext.User.Identity.IsAuthenticatedのようなものになります。あなたがそれを叩くつもりなら唯一の方法はあなたが脂肪指で何かをしている場合ですが、コンパイラはそれについてあなたに警告します。

+0

答えをいただきありがとうございます。 –

+0

Cookie認証ミドルウェアのテストは、実行中のWebプロセスのコンテキスト内での統合テストの一環としてのみ行うことができます。 HttpContextインタラクションの抽象概念は、あなたが示唆したと思うようにコードの残りの部分をテストするための鍵ですか? –

+0

はい。単体テストは、機能の小さな離散した「単位」に焦点を当てる必要があります。クッキーが設定されているということは実装の詳細なので、テストコードには関係ありません。 –

2

これは質問の文脈に直接答えるものではありませんが、テストのための代替方法を提供しています。いつ使用するのが簡単になります。

そこには、ASP.NETコアで利用可能な統合テストパッケージがあり、それについてのドキュメントはここで見つけることができます:

https://docs.asp.net/en/latest/testing/integration-testing.html

をお楽しみください!

関連する問題