[asp.net-mvc]单元测试调用会话对象的行动

标签: Asp.net-MVC .Net C#
发布时间: 2017/3/25 11:17:25
注意事项: 本文中文内容可能为机器翻译,如要查看英文原文请点击上面连接.

我怎么能单元测试使用会话对象,其体内的方法吗?

让我们说我有下列行动︰

[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:

我已经解决了类似的问题 (静态数据的访问器不是模拟友好-尤其是,使用 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 实例与一个 mock 对象之前调用 GetSearchResultGrid

理想情况下你会在某种程度上,使用依赖注入库和摆脱静态构造函数。

如果你能弄出一个办法来让你 ISessionData 实例的对象,而不是静止,甚至更好。 模拟对象框架往往是想创建一个新的模拟类型,为每个测试用例和模拟躺在从先前的测试是有点恶心。 我相信会话状态要反正可以全局使用您的会话,您可能不需要做任何事情很棘手,工作非静态对象。

赞助商