如何对在其体内使用会话对象的方法进行单元测试?
我们说我有以下行动:
[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); }
}
答案 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
实例化对象而不是静态,那就更好了。模拟对象框架倾向于为每个测试用例创建一个新的模拟类型,并且在之前的测试中放置模拟有点粗糙。我相信会话状态无论如何都将成为你的会话的全局,所以你可能不必做任何棘手的事情来使非静态对象工作。