41

.NETアセンブリのメソッドについて考えてみます。

public static string GetSecurityContextUserName()
{             
 //extract the username from request              
 string sUser = HttpContext.Current.User.Identity.Name;
 //everything after the domain     
 sUser = sUser.Substring(sUser.IndexOf("\\") + 1).ToLower();

 return sUser;      
}

Moqフレームワークを使用した単体テストからこのメソッドを呼び出したいと思います。このアセンブリは、Webフォームソリューションの一部です。単体テストは次のようになりますが、Moqコードがありません。

//arrange 
 string ADAccount = "BUGSBUNNY";
 string fullADName = "LOONEYTUNES\BUGSBUNNY"; 

 //act    
 //need to mock up the HttpContext here somehow -- using Moq.
 string foundUserName = MyIdentityBL.GetSecurityContextUserName();

 //assert
 Assert.AreEqual(foundUserName, ADAccount, true, "Should have been the same User Identity.");

質問

  • Moqを使用して、「MyDomain \ MyUser」のような値を持つ偽のHttpContextオブジェクトを配置するにはどうすればよいですか?
  • その偽物をで静的メソッドへの呼び出しと関連付けるにはどうすればよいMyIdentityBL.GetSecurityContextUserName()ですか?
  • このコード/アーキテクチャを改善する方法について何か提案はありますか?
4

7 に答える 7

46

Webformsは、この正確な理由でテストできないことで有名です。多くのコードは、asp.netパイプラインの静的クラスに依存する可能性があります。

MoqでこれをテストするにGetSecurityContextUserName()は、オブジェクトで依存性注入を使用するようにメソッドをリファクタリングする必要がありHttpContextBaseます。

HttpContextWrapperに常駐しSystem.Web.Abstractions、.Net3.5に同梱されています。これはHttpContextクラスのラッパーであり、を拡張します。次のようにHttpContextBase作成できます。HttpContextWrapper

var wrapper = new HttpContextWrapper(HttpContext.Current);

さらに良いことに、Moqを使用してHttpContextBaseをモックし、期待値を設定することができます。ログインしたユーザーなどを含みます。

var mockContext = new Mock<HttpContextBase>();

これが適切な場所にあると、を呼び出すことができGetSecurityContextUserName(mockContext.Object)、アプリケーションは静的WebFormsHttpContextとの結合がはるかに少なくなります。モックされたコンテキストに依存する多くのテストを行う場合は、Moqで使用するバージョンがあるScottHanselmanのMvcMockHelpersクラスを確認することを強くお勧めします。必要なセットアップの多くを便利に処理します。名前にもかかわらず、MVCでそれを行う必要はありません-使用するためにリファクタリングできる場合、私はWebformsアプリでそれを正常に使用しますHttpContextBase

于 2009-07-31T18:55:45.440 に答える
3

一般に、ASP.NET単体テストでは、HttpContext.Currentにアクセスするのではなく、依存性注入によって値が設定されるHttpContextBaseタイプのプロパティが必要です(Wompが提供する回答など)。

ただし、セキュリティ関連の関数をテストするには、(HttpContext.Current.Userの代わりに)Thread.CurrentThread.Principalを使用することをお勧めします。Thread.CurrentThreadを使用すると、Webコンテキストの外部でも再利用できるという利点があります(ASP.NETフレームワークは常に両方の値を同じに設定するため、Webコンテキストでも同じように機能します)。

次に、Thread.CurrentThread.Principalをテストするには、通常、Thread.CurrentThreadをテスト値に設定し、破棄時にリセットするスコープクラスを使用します。

using (new UserResetScope("LOONEYTUNES\BUGSBUNNY")) {
    // Put test here -- CurrentThread.Principal is reset when PrincipalScope is disposed
}

これは、標準の.NETセキュリティコンポーネント(コンポーネントが既知のインターフェイス(IPrincipal)と場所(Thread.CurrentThread.Principal)を持っている)によく適合し、Thread.CurrentThread.Principalを正しく使用/チェックするすべてのコードで機能します。 。

基本スコープクラスは次のようになります(ロールの追加などに必要に応じて調整します)。

class UserResetScope : IDisposable {
    private IPrincipal originalUser;
    public UserResetScope(string newUserName) {
        originalUser = Thread.CurrentPrincipal;
        var newUser = new GenericPrincipal(new GenericIdentity(newUserName), new string[0]);
        Thread.CurrentPrincipal = newUser;
    }
    public IPrincipal OriginalUser { get { return this.originalUser; } }
    public void Dispose() {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    protected virtual void Dispose(bool disposing) {
        if (disposing) {
            Thread.CurrentPrincipal = originalUser;
        }
    }
}

もう1つの方法は、標準のセキュリティコンポーネントの場所を使用する代わりに、挿入されたセキュリティの詳細を使用するようにアプリを作成することです。たとえば、GetCurrentUser()メソッドなどを使用してISecurityContextプロパティを追加し、それをアプリケーション全体で一貫して使用します。 Webアプリケーションのコンテキストでこれを行う場合は、事前に作成された挿入コンテキストHttpContextBaseを使用することもできます。

于 2012-01-30T22:34:25.740 に答える
3
[TestInitialize]
public void TestInit()
{
  HttpContext.Current = new HttpContext(new HttpRequest(null, "http://tempuri.org", null), new HttpResponse(null));
}

また、あなたは以下のようにmoqすることができます

var controllerContext = new Mock<ControllerContext>();
      controllerContext.SetupGet(p => p.HttpContext.Session["User"]).Returns(TestGetUser);
      controllerContext.SetupGet(p => p.HttpContext.Request.Url).Returns(new Uri("http://web1.ml.loc"));
于 2014-04-16T00:36:58.723 に答える
1

この http://haacked.com/archive/2007/06/19/unit-tests-web-code-without-a-web-server-using-httpsimulator.aspxをご覧ください

httpSimulatorクラスを使用すると、HttpContextをハンドラーに渡すことができます。

HttpSimulator sim = new HttpSimulator("/", @"C:\intepub\?")
.SimulateRequest(new Uri("http://localhost:54331/FileHandler.ashx?
ticket=" + myticket + "&fileName=" + path));

FileHandler fh = new FileHandler();
fh.ProcessRequest(HttpContext.Current);

HttpSimulatorは、HttpContextインスタンスを取得するために必要なものを実装します。したがって、ここでMoqを使用する必要はありません。

于 2012-03-13T12:52:20.377 に答える
1

ASP.NET MVC Coreでは、次のコードを使用してコントローラーをテストします。コントローラーは次の条件に依存しHttpContextます。

var controller = new HomeController();
controller.ControllerContext.HttpContext = new DefaultHttpContext();

これはサンプルの単体テストです。

[Test]
public void Test_HomeController_Index()
{
    // Arrange
    var controller = new HomeController();
    controller.ControllerContext.HttpContext = new DefaultHttpContext();

    // Act
    var result = controller.Index();

    // Assert
    var viewResult = result as ViewResult;
    Assert.IsNotNull(viewResult);
}
于 2021-04-22T14:00:33.123 に答える
0

(私たちが行っているように)CLRセキュリティモデルを使用している場合、テストを許可する場合は、いくつかの抽象化された関数を使用して現在のプリンシパルを取得および設定し、プリンシパルを取得または設定するたびにこれらを使用する必要があります。HttpContextこれを行うことで、関連する場所(通常はWeb上、および単体テストなどの他の場所の現在のスレッド上)でプリンシパルを取得/設定できます。これは次のようになります。

public static IPrincipal GetCurrentPrincipal()
{
    return HttpContext.Current != null ?
        HttpContext.Current.User :
        Thread.CurrentThread.Principal;
}

public static void SetCurrentPrincipal(IPrincipal principal)
{
     if (HttpContext.Current != null) HttpContext.Current.User = principal'
     Thread.CurrentThread.Principal = principal;
}

カスタムプリンシパルを使用する場合、これらはそのインターフェイスにかなりうまく統合できます。たとえば、以下でCurrentはを呼び出しGetCurrentPrincipalSetAsCurrentを呼び出しますSetCurrentPrincipal

public class MyCustomPrincipal : IPrincipal
{
    public MyCustomPrincipal Current { get; }
    public bool HasCurrent { get; }
    public void SetAsCurrent();
}
于 2009-07-31T19:14:34.933 に答える
0

これは、必要なものの単体テストにMoqを使用することとは実際には関係ありません。

一般に、私たちが取り組んでいるのは階層化アーキテクチャであり、プレゼンテーション層のコードは、実際にはUIに表示するためのものを配置するためだけのものです。この種のコードは単体テストではカバーされていません。残りのロジックはすべてビジネスレイヤーにあります。UIはWinFormsアプリケーションであり、必ずしもWebアプリケーションである必要はないため、プレゼンテーションレイヤー(つまり、HttpContextなどのUI固有の参照)に依存する必要はありません。 。

このようにして、Mockフレームワークをいじったり、HttpRequestsをシミュレートしようとしたりすることを回避できますが、それでも必要な場合がよくあります。

于 2009-07-31T19:20:36.763 に答える