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

Yur*_*han 1 .net c# 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);
}
Run Code Online (Sandbox Code Playgroud)

由于会话的原因,我进行了以下单元测试:

[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)));
}
Run Code Online (Sandbox Code Playgroud)

当然来自助手的方法:

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

Mer*_*ham 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;

        // ...
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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