2016-10-27 3 views
3

Я изо всех сил пытаюсь смоделировать нужный HttpContext для моих модульных тестов.Как создать HttpContext для моего модульного теста?

Я отвлек контроль сеанса от моего контроллера Mvc с помощью интерфейса SessionManager и реализовал его с помощью класса CookieSessionManager. (Стадия разработки).

CookieSessionManager использует HttpContext с помощью инжектированного одноэлементного HttpContextAccessor (в Startup.cs ConfigureServices).

Я использую аутентификацию Cookie, которая настроена в Startup.cs с app.UseCookieAuthentication.

Тестирование вручную в режиме отладки работает, как ожидалось

В MSUnit тесты, которые я написал для моего AccountController работы класса с MockSessionManager класса впрыскивается.

Настоящая проблема у меня есть с модульными тестами, которые я написал для моего класса 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 Тест Результат: Failed Test Продолжительность: 0: 00: 00,0433169

Результат StackTrace: в ClaimsWebAppTests.Identity.CookieSessionManagerTest.CookieSessionManager_Login_ValidUser_Authenticated_isTrue()

CookieSessionManagerTest.cs: строка 46 Сообщение результатов: Assert.IsTrue не удалось.

МОЙ КОД

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; 
     } 
    } 
} 

Start up.cs

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.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/ru/latest/testing/integration-testing.html – Brad

+0

Спасибо @Brad, я думаю, что это именно то, что я ищу. Я просто слишком старался сделать единичный тест из проблемы, когда речь идет о реализации. –

+1

его можно издеваться над HttpContext с изолятором TypeMock, например: https://www.typemock.com/docs?book=Isolator&page=Documentation%2FHtmlDocs%2Fsample1fakinghttpcontextandmodelstate.htm –

ответ

6

К сожалению, это практически невозможно протестировать с HttpContext. Это запечатанный класс, который не использует никаких интерфейсов, поэтому вы не можете издеваться над ним. Обычно лучше всего отвлечь код, который работает с HttpContext, а затем проверить только ваш другой, более специфичный для приложения код.

Похоже, что вы уже это сделали через HttpContextAccessor, но вы используете его неправильно. Во-первых, вы раскрываете экземпляр HttpContext, который в значительной степени поражает всю цель. Этот класс должен иметь возможность вернуть что-то вроде User.Identity.IsAuthenticated самостоятельно, например: httpContextAccessor.IsAuthenticated. Внутренне свойство будет обращаться к частному HttpContext экземпляру и просто вернуть результат.

Как только вы используете его таким образом, вы можете затеммать HttpContextAccessor, чтобы просто вернуть то, что вам нужно для ваших тестов, и вам не нужно беспокоиться о том, чтобы предоставить ему экземпляр HttpContext.

Конечно, это означает, что есть еще непроверенный код, а именно методы доступа, которые работают с HttpContext, но они, как правило, очень прямолинейны. Например, код для IsAuthenticated будет просто чем-то вроде return httpContext.User.Identity.IsAuthenticated. Единственный способ, которым вы собираетесь это испортить, - это то, что вы что-то приложите, но компилятор предупредит вас об этом.

+0

Спасибо за ваш ответ, я занят его перевариванием. –

+0

Правильно ли тогда было бы сказать, что тестирование промежуточного ПО для проверки подлинности Cookie может быть достигнуто только в рамках интеграционного тестирования в контексте запущенного веб-процесса; и что абстракция взаимодействия HttpContext является ключом к модулю тестирования остальной части кода, как я думаю, вы предложили? –

+0

Да. В ваших модульных тестах следует сосредоточиться на небольших дискретных «единицах» функциональности, отсюда и название. Тот факт, что cookie был установлен, является детальностью реализации и не имеет значения для вашего тестового кода. –

2

Это не отвечает непосредственно на вопрос, но предоставляет альтернативный метод тестирования, и когда вы начинаете использовать его, жизнь становится намного проще.

Существует пакет интеграционного тестирования для ASP.NET Ядра и документации о ней можно найти здесь:

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

Наслаждайтесь!

Смежные вопросы