9

プロジェクトをASP.NET MVC 2から3にアップグレードしたいと考えていました。テストの大半は成功しましたが、失敗したものはValueProviderFactories.Factories.GetValueProvider(context)です。ASP.NET MVC3でValueProviderFactoriesを単体テストする方法は?

ここでは、問題を明らかにする簡単なテストクラスを示します。

[TestFixture] 
public class FailingTest 
{ 
    [Test] 
    public void Test() 
    { 
    var type = typeof(string); 
    // any controller 
    AuthenticationController c = new AuthenticationController(); 
    var httpContext = new Mock<HttpContextBase>(); 
    var context = c.ControllerContext = new ControllerContext(httpContext.Object, new RouteData(), c); 

    IModelBinder converter = ModelBinders.Binders.GetBinder(type); 
    var bc = new ModelBindingContext 
    { 
     ModelName = "testparam", 
     ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, type), 
     ValueProvider = ValueProviderFactories.Factories.GetValueProvider(context) 
    }; 
    Console.WriteLine(converter.BindModel(context, bc)); 
    } 
} 

例外「オブジェクト参照がオブジェクトのインスタンスに設定されていません」。 ValueProviderFactories.Factories.GetValueProvider(context)が呼び出されるとスローされます。私はそれが例外とのこぎり投げた理由を知りたいと思った

Microsoft.Web.Infrastructure.dll!Microsoft.Web.Infrastructure.DynamicValidationHelper.ValidationUtility.CollectionReplacer.GetUnvalidatedCollections(System.Web.HttpContext context) + 0x23 bytes 
Microsoft.Web.Infrastructure.dll!Microsoft.Web.Infrastructure.DynamicValidationHelper.ValidationUtility.GetUnvalidatedCollections(System.Web.HttpContext context, out System.Collections.Specialized.NameValueCollection form, out System.Collections.Specialized.NameValueCollection queryString, out System.Collections.Specialized.NameValueCollection headers, out System.Web.HttpCookieCollection cookies) + 0xbe bytes  
System.Web.WebPages.dll!System.Web.Helpers.Validation.Unvalidated(System.Web.HttpRequest request) + 0x73 bytes 
System.Web.WebPages.dll!System.Web.Helpers.Validation.Unvalidated(System.Web.HttpRequestBase request) + 0x25 bytes 
System.Web.Mvc.DLL!System.Web.Mvc.FormValueProviderFactory..ctor.AnonymousMethod__0(System.Web.Mvc.ControllerContext cc) + 0x5a bytes 
System.Web.Mvc.DLL!System.Web.Mvc.FormValueProviderFactory.GetValueProvider(System.Web.Mvc.ControllerContext controllerContext) + 0xa0 bytes  
System.Web.Mvc.DLL!System.Web.Mvc.ValueProviderFactoryCollection.GetValueProvider.AnonymousMethod__7(System.Web.Mvc.ValueProviderFactory factory) + 0x4a bytes 
System.Core.dll!System.Linq.Enumerable.WhereSelectEnumerableIterator<System.Web.Mvc.ValueProviderFactory,<>f__AnonymousType2<System.Web.Mvc.ValueProviderFactory,System.Web.Mvc.IValueProvider>>.MoveNext() + 0x24d bytes 
System.Core.dll!System.Linq.Enumerable.WhereSelectEnumerableIterator<<>f__AnonymousType2<System.Web.Mvc.ValueProviderFactory,System.Web.Mvc.IValueProvider>,System.Web.Mvc.IValueProvider>.MoveNext() + 0x2ba bytes 
mscorlib.dll!System.Collections.Generic.List<System.Web.Mvc.IValueProvider>.List(System.Collections.Generic.IEnumerable<System.Web.Mvc.IValueProvider> collection) + 0x1d8 bytes  
System.Core.dll!System.Linq.Enumerable.ToList<System.Web.Mvc.IValueProvider>(System.Collections.Generic.IEnumerable<System.Web.Mvc.IValueProvider> source) + 0xb5 bytes 
System.Web.Mvc.DLL!System.Web.Mvc.ValueProviderFactoryCollection.GetValueProvider(System.Web.Mvc.ControllerContext controllerContext) + 0x24d bytes 
test.DLL!FailingTest.Test() Line 31 + 0xf9 bytes C# 

::スタックトレースは次のようになりますので、

public static ValidationUtility.UnvalidatedCollections GetUnvalidatedCollections(HttpContext context) 
{ 
    return (ValidationUtility.UnvalidatedCollections) context.Items[_unvalidatedCollectionsKey]; 
} 

を我々はHttpContext.Currentに依存していたとき、私たちは、過去に戻ってきましたか?それを回避するには?

+0

私は同じ問題があります。いい質問のために+1。 –

+0

私は同じ必要があったので、ありがとう。言及しなければならない点の1つは、新しいRouteData()を設定するだけでエラーが発生する可能性があります。それを克服するために、私は "コントローラ"と "アクション"キー/値をroutedataに追加しなければなりませんでした。 – uadrive

答えて

8

これは、HttpContextを無視するValueProviderをプロキシすることで簡単に解決できます。

私はブログ投稿のすべてについて説明しました:Unit test actions with ValueProviderFactories in ASP.NET MVC3

キーは、このコードです:

public static class ValueProviderFactoresExtensions { 
    public static ValueProviderFactoryCollection ReplaceWith<TOriginal>(this ValueProviderFactoryCollection factories, Func<ControllerContext, NameValueCollection> sourceAccessor) { 
     var original = factories.FirstOrDefault(x => typeof(TOriginal) == x.GetType()); 
     if (original != null) { 
      var index = factories.IndexOf(original); 
      factories[index] = new TestValueProviderFactory(sourceAccessor); 
     } 
     return factories; 
    } 

    class TestValueProviderFactory : ValueProviderFactory { 
     private readonly Func<ControllerContext, NameValueCollection> sourceAccessor; 


     public TestValueProviderFactory(Func<ControllerContext, NameValueCollection> sourceAccessor) { 
      this.sourceAccessor = sourceAccessor; 
     } 


     public override IValueProvider GetValueProvider(ControllerContext controllerContext) { 
      return new NameValueCollectionValueProvider(sourceAccessor(controllerContext), CultureInfo.CurrentCulture); 
     } 
    }   
} 

だから、として使用することができます。コメントで述べたように:

ValueProviderFactories.Factories 
    .ReplaceWith<FormValueProviderFactory>(ctx => ctx.HttpContext.Request.Form) 
    .ReplaceWith<QueryStringValueProviderFactory>(ctx => ctx.HttpContext.Request.QueryString); 

実際には非常に簡単:)

UPDATEました次のことを覚えておいてください。

  1. をnull以外の値に設定します。そうでない場合、JsonValueProviderFactoryは例外をスローします。私はモックを作成し、そこでデフォルト値を設定するほうが好きです。
  2. は、バインディング時に使用できるようにHttpFileCollectionValueProviderFactoryを置き換えます。
  3. プロジェクトに存在する他の依存関係に注意してください。
+1

良い仕事!注意:ctx.HttpContext.Request.ContentTypeプロパティをnull以外の値に設定する必要があります。そうしないと、JsonValueProviderFactoryが例外をスローします。 – dkl

+0

問題を解決しました。ありがとう:) – stej

+1

これを動作させるには、 'HttpFileCollectionValueProviderFactory'も置き換えなければなりませんでした。 –

0

単体テスト内からValueProviderFactories.Factories、ModelBinders.Binders、またはその他の静的アクセサを呼び出すことは避けてください。これはModelBindingContext.ValueProviderが存在する理由です。静的なデフォルト(MVCパイプラインが実行されていることを前提としています)に頼るのではなく、独自の作成の模擬IValueProviderを提供することができます。

+4

'IValueProvider'は、ばかげた仕事量です。何十ものASP.NET MVC機能を再実装する必要があります。どのようにテスト可能であったかは面白くなくて、今はそうではありません。 –

関連する問題