45

shanselmannのMvcMockHelperクラスを使用してMoqを使用してHttpContextのものをモックアップしていますが、MVCコントローラーでモックされたセッションオブジェクトに何かを割り当てて、検証のために単体テストで同じ値を読み取ることができるという問題があります目的。

私の質問は、モックセッションオブジェクトにストレージコレクションを割り当てて、session ["UserName"] ="foo"などのコードが"foo"値を保持し、単体テストで使用できるようにする方法です。

4

7 に答える 7

65

Scott Hanselman のMVCMockHelperから始めて、小さなクラスを追加し、以下に示す変更を加えて、コントローラーが Session を正常に使用できるようにし、単体テストでコントローラーによって設定された値を検証しました。

/// <summary>
/// A Class to allow simulation of SessionObject
/// </summary>
public class MockHttpSession : HttpSessionStateBase
{
    Dictionary<string, object> m_SessionStorage = new Dictionary<string, object>();

    public override object this[string name]
    {
        get { return m_SessionStorage[name]; }
        set { m_SessionStorage[name] = value; }
    }
}

//In the MVCMockHelpers I modified the FakeHttpContext() method as shown below
public static HttpContextBase FakeHttpContext()
{
    var context = new Mock<HttpContextBase>();
    var request = new Mock<HttpRequestBase>();
    var response = new Mock<HttpResponseBase>();
    var session = new MockHttpSession();
    var server = new Mock<HttpServerUtilityBase>();

    context.Setup(ctx => ctx.Request).Returns(request.Object);
    context.Setup(ctx => ctx.Response).Returns(response.Object);
    context.Setup(ctx => ctx.Session).Returns(session);
    context.Setup(ctx => ctx.Server).Returns(server.Object);

    return context.Object;
}

//Now in the unit test i can do
AccountController acct = new AccountController();
acct.SetFakeControllerContext();
acct.SetBusinessObject(mockBO.Object);

RedirectResult results = (RedirectResult)acct.LogOn(userName, password, rememberMe, returnUrl);
Assert.AreEqual(returnUrl, results.Url);
Assert.AreEqual(userName, acct.Session["txtUserName"]);
Assert.IsNotNull(acct.Session["SessionGUID"]);

完璧ではありませんが、テストには十分です。

于 2009-03-27T18:23:31.390 に答える
39

Moq 3.0.308.2 を使用したユニット テストでのアカウント コントローラーのセットアップ例を次に示します。

    private AccountController GetAccountController ()
    {
      .. setup mocked services..

      var accountController = new AccountController (..mocked services..);

      var controllerContext = new Mock<ControllerContext> ();
      controllerContext.SetupGet(p => p.HttpContext.Session["test"]).Returns("Hello World");
      controllerContext.SetupGet(p => p.HttpContext.User.Identity.Name).Returns(_testEmail);
      controllerContext.SetupGet(p => p.HttpContext.Request.IsAuthenticated).Returns(true);
      controllerContext.SetupGet(p => p.HttpContext.Response.Cookies).Returns(new HttpCookieCollection ());

      controllerContext.Setup (p => p.HttpContext.Request.Form.Get ("ReturnUrl")).Returns ("sample-return-url");
      controllerContext.Setup (p => p.HttpContext.Request.Params.Get ("q")).Returns ("sample-search-term");

      accountController.ControllerContext = controllerContext.Object;

      return accountController;
    }

次に、コントローラーメソッド内で、次は「Hello World」を返す必要があります

string test = Session["test"].ToString ();
于 2009-03-13T20:21:06.330 に答える
3

@RonnBlackが投稿した回答よりも少し手の込んだモックを作成しました

public class HttpSessionStateDictionary : HttpSessionStateBase
{
    private readonly NameValueCollection keyCollection = new NameValueCollection();

    private readonly Dictionary<string, object> _values = new Dictionary<string, object>();

    public override object this[string name]
    {
        get { return _values.ContainsKey(name) ? _values[name] : null; }
        set { _values[name] = value; keyCollection[name] = null;}
    }

    public override int CodePage
    {
        get { throw new NotImplementedException(); }
        set { throw new NotImplementedException(); }
    }

    public override HttpSessionStateBase Contents
    {
        get { throw new NotImplementedException(); }
    }

    public override HttpCookieMode CookieMode
    {
        get { throw new NotImplementedException(); }
    }

    public override int Count
    {
        get { return _values.Count; }
    }

     public override NameObjectCollectionBase.KeysCollection Keys
{
    get { return keyCollection.Keys; }
}

    public Dictionary<string, object> UnderlyingStore
    {
        get { return _values; }
    }

    public override void Abandon()
    {
        _values.Clear();
    }

    public override void Add(string name, object value)
    {
        _values.Add(name, value);
    }

    public override void Clear()
    {
        _values.Clear();
    }

    public override void CopyTo(Array array, int index)
    {
        throw new NotImplementedException();
    }

    public override bool Equals(object obj)
    {
        return _values.Equals(obj);
    }

    public override IEnumerator GetEnumerator()
    {
        return _values.GetEnumerator();
    }

    public override int GetHashCode()
    {
        return (_values != null ? _values.GetHashCode() : 0);
    }

    public override void Remove(string name)
    {
        _values.Remove(name);
    }

    public override void RemoveAll()
    {
        _values.Clear();
    }

    public override void RemoveAt(int index)
    {
        throw new NotImplementedException();
    }

    public override string ToString()
    {
        return _values.ToString();
    }

    public bool Equals(HttpSessionStateDictionary other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return Equals(other._values, _values);
    }
}
于 2011-02-24T23:38:01.830 に答える
0

モックが返す特定の値を使用して、モックに期待を設定できると思います。モックは実際の偽物として使用されるのではなく、動作をアサートできるものとして使用されます。

テスト中に別の実装を提供でき、実行時にHttpContextセッションアイテムを返すことができるセッションをラップできるアダプターを実際に探しているようですね?

これは理にかなっていますか?

于 2009-02-11T14:37:39.207 に答える