单元测试调用会话对象的操作

时间:2011-10-03 06:25:41

标签: c# .net asp.net-mvc

如何对在其体内使用会话对象的方法进行单元测试?

我们说我有以下行动:

[HttpPost]
public JsonResult GetSearchResultGrid(JqGridParams gridParams, Guid campaignId, string queryItemsString)
{
    var queryItems = new JavaScriptSerializer().Deserialize<IList<FilledQueryItem>>(queryItemsString);
    IPageData pageData = gridParams.ToPageData();
    var extraFieldLinker = SessionHandler.CurrentExtraFieldsLinker;
    var searchParams = new SearchParamsModel(extraFieldLinker, queryItems);
    IList<CustomerSearchResultRow> searchResults = null;
    searchResults = _customerService.SearchCustomersByUrlAndCampaign(campaignId,
        searchParams.SearchString,
        searchParams.AddressFilterPredicate,
        pageData);
    return GetGridData<CustomerSearchResultGridDefinition, CustomerSearchResultRow>(searchResults, pageData);
}

由于会话的原因,我做了以下单元测试但到目前为止失败了:

[Test]
public void CanGetSearchResultGrid()
{
    //Initialize
    var mockJqGridParams = new Mock<JqGridParams>();
    var mockPageData = new Mock<IPageData>();
    IPagedList<CustomerSearchResultRow> mockPagedResult = new PagedList<CustomerSearchResultRow>(mockPageData.Object);
    var guid= Guid.NewGuid();
    const string searchString =
        "[{\"Caption\":\"FirstName\",\"ConditionType\":\"contains\",\"Value\":\"d\",\"NextItem\":\"Last\"}]";
    Func<Address,bool> addressFilterPredicate = (x => true);

    //Setup
    mockJqGridParams.Setup(x => x.ToPageData()).Returns(mockPageData.Object);
    _customerService.Setup(x => x.SearchCustomersByUrlAndCampaign(guid, searchString, addressFilterPredicate, mockPageData.Object))
        .Returns(mockPagedResult);

    //Call
    var result = _homeController.GetSearchResultGrid(mockJqGridParams.Object, guid, searchString);

    mockJqGridParams.Verify(x => x.ToPageData(), Times.Once());
    _customerService.Verify(x => x.SearchCustomersByUrlAndCampaign(guid, searchString, addressFilterPredicate, mockPageData.Object)
        , Times.Once());

    //Verify
    Assert.That(result, Is.Not.Null);
    Assert.That(result, Is.TypeOf(typeof(JsonResult)));
}

当然来自助手的方法:

   public static ExtraFieldsLinker CurrentExtraFieldsLinker
    {
        get
        {
            object extraFieldLinker = GetSessionObject(EXTRA_FIELDS_LINKER);
            return extraFieldLinker as ExtraFieldsLinker;
        }
        set { SetSessionObject(EXTRA_FIELDS_LINKER, value); }
    }

1 个答案:

答案 0 :(得分:5)

我已经解决了类似的问题(使用非模拟友好的静态数据访问器,特别是HttpContext.Current),将访问包装在另一个对象中,并通过接口访问它。你可以这样做:

pubic interface ISessionData
{
    ExtraFieldsLinker CurrentExtraFieldsLinker { get; set; }
}

public class SessionDataImpl : ISessionData
{
    ExtraFieldsLinker CurrentExtraFieldsLinker
    {
        // Note: this code is somewhat bogus,
        // since I think these are methods of your class.
        // But it illustrates the point.  You'd put all the access here
        get { return (ExtraFieldsLinker)GetSessionObject(EXTRA_FIELDS_LINKER); }
        set { SetSessionObject(EXTRA_FIELDS_LINKER, value); }
    }
}

public class ClassThatContainsYourAction
{
    static ClassThatContainsYourAction()
    {
        SessionData = new SessionDataImpl();
    }

    public static ISessionData SessionData { get; private set; }

    // Making this access very ugly so you don't do it by accident
    public void SetSessionDataForUnitTests(ISessionData sessionData)
    {
        SessionData = sessionData;
    }

    [HttpPost]
    public JsonResult GetSearchResultGrid(JqGridParams gridParams,
        Guid campaignId, string queryItemsString)
    {
        var queryItems = // ...
        IPageData pageData = // ...

        // Access your shared state only through SessionData
        var extraFieldLinker = SessionData.CurrentExtraFieldsLinker;

        // ...
    }
}

然后,您的单元测试可以在调用ISessionData之前将GetSearchResultGrid实例设置为模拟对象。

理想情况下,您在某些时候使用依赖注入库,并摆脱静态构造函数。

如果你能找到一种方法来使你的ISessionData实例化对象而不是静态,那就更好了。模拟对象框架倾向于为每个测试用例创建一个新的模拟类型,并且在之前的测试中放置模拟有点粗糙。我相信会话状态无论如何都将成为你的会话的全局,所以你可能不必做任何棘手的事情来使非静态对象工作。