2

私が関わっているプロジェクトで単体テストを使用したいと思います。このソリューションには、UIレイヤー、ドメインロジックレイヤー、データベースベースがすべて物理的に分離されたdllに分離されているため、現在ではかなり標準的です。

ドメインレイヤーには、を利用するクラスがあります

new HttpContextWrapper(HttpContext.Current)

動的メニューのURLをコンパイルするための現在のリクエストのコンテキストを取得します。また、このHttpContext.Currentは常に設定されているため、Web環境で実行すると、予想どおりすべてが正常に機能します。

ただし、コントローラーの単体テストを行うと、このコード行にヒットし、Null参照例外が発生します。

いくつかの調査の後、Mockを使用して偽のHttpコンテキストを作成し、次のようにコントローラーを作成するときにこれを設定することを提案する多くの記事があります。

var httpContext = FakeHttpContext();
ControllerContext context = new ControllerContext(new RequestContext(httpContext, new RouteData()), controller);
controller.ControllerContext = context;

しかし、これでもHttpContxt.Currentは変更されず、null参照例外が発生します。

何が間違っているのですか/この例外を解決するにはどうすればよいですか?

4

6 に答える 6

2

次のコードを使用して、HttpContext.Currentを作成できます。

[SetUp]
public void Test_SetUp()
{
    var context = CreateHttpContext("index.aspx", "http://tempuri.org/index.aspx", null);
    var result = RunInstanceMethod(Thread.CurrentThread, "GetIllogicalCallContext", new object[] { });
    SetPrivateInstanceFieldValue(result, "m_HostContext", context);
}

プライベートメソッド:

private static HttpContext CreateHttpContext(string fileName, string url, string queryString)
    {
        var sb = new StringBuilder();
        var sw = new StringWriter(sb);
        var hres = new HttpResponse(sw);
        var hreq = new HttpRequest(fileName, url, queryString);
        var httpc = new HttpContext(hreq, hres);
        return httpc;
    }

    private static object RunInstanceMethod(object source, string method, object[] objParams)
    {
        var flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
        var type = source.GetType();
        var m = type.GetMethod(method, flags);
        if (m == null)
        {
            throw new ArgumentException(string.Format("There is no method '{0}' for type '{1}'.", method, type));
        }

        var objRet = m.Invoke(source, objParams);
        return objRet;
    }

    public static void SetPrivateInstanceFieldValue(object source, string memberName, object value)
    {
        var field = source.GetType().GetField(memberName, BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance);
        if (field == null)
        {
            throw new ArgumentException(string.Format("Could not find the private instance field '{0}'", memberName));
        }

        field.SetValue(source, value);
    }
于 2012-12-03T03:20:34.533 に答える
1

MvcContrib TestHelperを見たことがありますか?私はそれを使ったことがありませんが、他の人がそれで成功しているのを見ました。これは、いくつかのさらなる例を含むブログ投稿です。

于 2012-12-03T02:52:44.733 に答える
1

ドメインクラスで使用する必要がHttpContextBaseあります。そのドメインクラスをコントローラーに挿入することをお勧めします。そのため、単体テスト中に、テスト対象のコントローラーに偽のhttpcontextを含むドメインクラスのインスタンスを提供できます。

元。

public class DomainClass
{
  private readonly HttpContextBase _httpContextBase;

  // unit testing
  public DomainClass(HttpContextBase httpContextBase)
  {
     _httpContextBase = httpContextBase;
  }

  public DomainClass()
  {
     _httpContextBase = new HttpContextWrapper(HttpContext.Current);
  }   
}
于 2012-12-03T15:19:00.353 に答える
0

この方法でHttpContext.Currentを使用することはできません。以前のギグの1つで、これを説明するために、別のラッパークラスを作成し、そのプロパティにアクセスする必要がありました。

静力学の使用は、単体テストで非常に一般的な問題です。

更新のリクエストごとに:

public static class HttpContextContainer
{
     private static HttpContext _ctx;
     public static HttpContext Current 
     {
         get 
         { 
             if (_ctx == null) _ctx = HttpContext.Current;
             return _ctx;
         }
         set
         {
             _ctx = value;
         }
     }
}

次に、使用法で次のようなことを行う場合があります。

new HttpContextWrapper(HttpContextContainer.Current)

と:

var httpContext = FakeHttpContext();
HttpContextContainer.Current = httpContext;
ControllerContext context = new ControllerContext(new RequestContext(httpContext, new RouteData()), controller);
controller.ControllerContext = context;
于 2012-12-03T01:35:13.137 に答える
0

ドメインレイヤーには、新しいHttpContextWrapper(HttpContext.Current)を利用するクラスがあります。

デザインに問題があると思います。

ドメインレイヤーにGUIコンテンツの知識が必要なのはなぜですか?

コントローラーコンストラクターで引数を作成してIHttpContextWrapperを受け入れ、モックラッパーを渡すことができるようにすることができます。そのようです:

public MyController()
  :this(new HttpContextWrapper())
{
  ....
}

public MyController(IHttpContextWrapper contextWrapper)
{
  ....
}

そして、次のようなテスト

var context = new Mock<IHttpContextWrapper>();
// setup context
var controller = new MyController(context.Object);
....

次に、コントローラーを変更して、ドメインレイヤーに必要な情報をすべて渡します。

于 2012-12-03T01:46:26.240 に答える
0

注意話

MSフェイクを使用しての動作をシムすることに注意してくださいHttpContext.Current { get; }

の呼び出しHttpContext.Current { set; }は成功しますが、設定値は使用できなくなります。

[TestInitialize]MSTestベースのテストでこれを使用して設定する人がいました。

ShimHttpContext.CurrentGet = () => new ShimHttpContext() 
{ 
  SessionGet = () => new ShimHttpSessionState() 
  { 
    ItemGetString = (name) => 
    { 
      return null; 
    } 
  } 
}; 
于 2021-01-06T16:35:58.527 に答える