2017-01-04 9 views
1

私はAutofacとOWINをゼロからビルドしたWebAPIプロジェクトで使用しています(VS2015で利用可能な完全なWebAPIテンプレートに加えて)。確かに私はこのようにするのが初めてです。ユニットテストプロジェクトでユニットテストでWebAPI2を使用しているRequestContext.PrincipalのAutofac DI

は、私はユニットテストの開始時にOWINスタートアップクラスを設定し、次のよう

WebApp.Start<Startup>("http://localhost:9000/") 

スタートアップクラスは次のとおりです。

[assembly: OwinStartup(typeof(API.Specs.Startup))] 

namespace API.Specs 
{ 
    public class Startup 
    { 
     public void Configuration(IAppBuilder appBuilder) 
     { 
      var config = new HttpConfiguration(); 
      //config.Filters.Add(new AccessControlAttribute()); 
      config.Services.Replace(typeof(IAssembliesResolver), new CustomAssembliesResolver()); 

      config.Formatters.JsonFormatter.SerializerSettings = Serializer.Settings; 
      config.MapHttpAttributeRoutes(); 

      // Autofac configuration 
      var builder = new ContainerBuilder(); 

      // Unit of Work 
      var unitOfWork = new Mock<IUnitOfWork>(); 
      builder.RegisterInstance(unitOfWork.Object).As<IUnitOfWork>(); 

      // Principal 
      var principal = new Mock<IPrincipal>(); 
      principal.Setup(p => p.IsInRole("admin")).Returns(true); 
      principal.SetupGet(p => p.Identity.Name).Returns('test.user'); 
      principal.SetupGet(p => p.Identity.IsAuthenticated).Returns(true); 

      Thread.CurrentPrincipal = principal.Object; 
      if (HttpContext.Current != null) 
      { 
       HttpContext.Current.User = new GenericPrincipal(principal.Object.Identity, null); 
      } 

      builder.Register(c => principal).As<IPrincipal>(); 

      . 
      . 
      . 
      // Set up dependencies for Controllers, Services & Repositories 
      . 
      . 
      . 

      var container = builder.Build(); 
      config.DependencyResolver = new AutofacWebApiDependencyResolver(container); 
      config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always; 

      appBuilder.UseWebApi(config); 
     } 

     private static void RegisterAssemblies<TModel, TController, TService, TRepoClass, TRepoInterface>(ref ContainerBuilder builder, ref Mock<IUnitOfWork> unitOfWork) 
      where TModel : class 
      where TRepoClass : class 
      where TService : class 
     { 
      RegisterController<TController>(ref builder); 
      var repositoryInstance = RegisterRepository<TRepoClass, TRepoInterface>(ref builder); 
      RegisterService<TService>(ref builder, ref unitOfWork, repositoryInstance); 
     } 

     private static void RegisterController<TController>(ref ContainerBuilder builder) 
     { 
      builder.RegisterApiControllers(typeof(TController).Assembly); 
     } 

     private static object RegisterRepository<TRepoClass, TRepoInterface>(ref ContainerBuilder builder) 
      where TRepoClass : class 
     { 
      var constructorArguments = new object[] { DataContexts.Instantiate }; 
      var repositoryInstance = Activator.CreateInstance(typeof(TRepoClass), constructorArguments); 
      builder.RegisterInstance(repositoryInstance).As<TRepoInterface>(); 

      return repositoryInstance; 
     } 

     private static void RegisterService<TService>(ref ContainerBuilder builder, ref Mock<IUnitOfWork> unitOfWork, object repositoryInstance) 
      where TService : class 
     { 
      var constructorArguments = new[] { repositoryInstance, unitOfWork.Object}; 
      var serviceInstance = Activator.CreateInstance(typeof(TService), constructorArguments); 

      builder.RegisterAssemblyTypes(typeof(TService).Assembly) 
       .Where(t => t.Name.EndsWith("Service")) 
       .AsImplementedInterfaces().InstancePerRequest(); 

      builder.RegisterInstance(serviceInstance); 
     } 
    } 
} 

サイドノート:理想的には私さまざまなユーザーをコントローラに渡すことができるように、テストの一環としてPrincipleを設定したいと思いますが、スタートアップクラスのCurrentPrincipal/Userの設定を絶対に維持する必要がある場合は、回避できます。

スタートアップクラスはDIを使用してコントローラにアクセスしてもうまく動作しますが、RequestContext.Principalのプリンシパルは設定されません。常にnullです。次のように私は意図要求コンテキストを使用する方法は次のとおりです。

[HttpGet] 
[Route("path/{testId}")] 
[ResponseType(typeof(Test))] 
public IHttpActionResult Get(string testId) 
{ 
    return Ok(_service.GetById(testId, RequestContext.Principal.Identity.Name)); 
} 

は私も回避策として、私のコントローラのコンストラクタに嘲笑主要なクラスを注入しようとした - 一般的な方法で表示されているように私は、同じ方法を使用しましたDIを使用して私のサービスをセットアップします。繰り返しますが、私はコンストラクタにnullしかありません。

この時点で私はこの問題で約1日座っていて、私の髪を引き出しています。どんな助けもありがとう。前もって感謝します。

答えて

1

私はDIでこれを行うことを避けたいと思います。要求コンテキストでプリンシパルを設定し、プリンシパルをコンストラクタに挿入しないでください。

はここで、それは私だった場合、私がしたいものです。

まず、私はをからかっ必要としないものを模擬しません。すなわち、あなたのIIdentityの実装は実際には実際のオブジェクトである可能性があります。

private static IPrincipal CreatePrincipal() 
{ 
    var identity = new GenericIdentity("test.user", "test"); 
    var roles = new string[] { "admin" }; 
    return new GenericPrincipal(identity); 
} 

次に、あなたがあなたのテストアプリケーションによって処理する各「要求」でセットアップを実行する必要があります。スタートアップクラス全体とすべてを使用しているので、これは「ユニットテスト」よりも「統合テスト」だと思っています。そのため、プリンシパルを一度設定して完了することはできません。実際の認証アクションのように、すべての要求に対して実行する必要があります。

これを行う最も簡単な方法は、a simple delegating handlerです。

public class TestAuthHandler : DelegatingHandler 
{ 
    protected override async Task<HttpResponseMessage> SendAsync(
    HttpRequestMessage request, CancellationToken cancellationToken) 
    { 
    // Set the principal. Whether you set the thread principal 
    // is optional but you should really use the request context 
    // principal exclusively when checking permissions. 
    request.GetRequestContext().Principal = CreatePrincipal(); 

    // Let the request proceed through the rest of the pipeline. 
    return await base.SendAsync(request, cancellationToken); 
    } 
} 

最後に、あなたのStartupクラスのHttpConfigurationパイプラインにそのハンドラを追加します。

config.MessageHandlers.Add(new TestAuthHandler()); 

これでいいはずです。リクエストはその認証ハンドラを通過し、プリンシパルが割り当てられます。

+0

こんにちはトラビス。後見では、あなたの解決策はかなり明白です。とてもうまく説明してくれてありがとう。今すぐあなたに戻ってくれたことを謝罪しました。私は回避策を使ってプロジェクトを進めなければなりませんでした。 – Ebbs

関連する問題