单元测试它调用会话对象的动作, [英] Unit-testing an action which calls session object
问题描述
我怎么能单元测试,它使用一个会话对象的身体内部的方法是什么?
让我们说我有以下作用:
[HttpPost]
公共JsonResult GetSearchResultGrid(JqGridParams gridParams中GUID campaignId,串queryItemsString)
{
VAR queryItems =新JavaScriptSerializer()反序列化<。IList的< FilledQueryItem>>(queryItemsString);
IPageData pageData = gridParams.ToPageData();
VAR extraFieldLinker = SessionHandler.CurrentExtraFieldsLinker;
VAR searchParams =新SearchParamsModel(extraFieldLinker,queryItems);
IList的< CustomerSearchResultRow>的SearchResult = NULL;
的SearchResult = _customerService.SearchCustomersByUrlAndCampaign(campaignId,
searchParams.SearchString,
searchParams.AddressFilter predicate,
pageData);
返回GetGridData< CustomerSearchResultGridDefinition,CustomerSearchResultRow>(SearchResult所,pageData);
}
我做了以下的单元测试其失败的,到目前为止,因为会话的事情:
[测试]
公共无效CanGetSearchResultGrid()
{
//初始化
VAR mockJqGridParams =新的模拟< JqGridParams>();
VAR mockPageData =新的模拟< IPageData>();
IPagedList< CustomerSearchResultRow> mockPagedResult =新PagedList< CustomerSearchResultRow>(mockPageData.Object);
变种GUID = Guid.NewGuid();
常量字符串searchString =
"[{\"Caption\":\"FirstName\",\"ConditionType\":\"contains\",\"Value\":\"d\",\"NextItem\":\"Last\"}]";
FUNC<地址,布尔> addressFilter predicate =(X =>真正的);
//建立
mockJqGridParams.Setup(X => x.ToPageData())返回(mockPageData.Object);
_customerService.Setup(X => x.SearchCustomersByUrlAndCampaign(GUID,searchString,addressFilter predicate,mockPageData.Object))
.Returns(mockPagedResult);
//呼叫
VAR的结果= _homeController.GetSearchResultGrid(mockJqGridParams.Object,GUID,searchString);
mockJqGridParams.Verify(X => x.ToPageData(),Times.Once());
_customerService.Verify(X => x.SearchCustomersByUrlAndCampaign(GUID,searchString,addressFilter predicate,mockPageData.Object)
,Times.Once());
//校验
Assert.That(结果,Is.Not.Null);
Assert.That(结果,Is.TypeOf(typeof运算(JsonResult)));
}
和从课程的辅助方法:
公共静态ExtraFieldsLinker CurrentExtraFieldsLinker
{
得到
{
反对extraFieldLinker = GetSessionObject(EXTRA_FIELDS_LINKER);
返回extraFieldLinker为ExtraFieldsLinker;
}
集合{SetSessionObject(EXTRA_FIELDS_LINKER,价值); }
}
我已经解决了类似的问题(使用静态数据访问未嘲弄友好的 - 尤其是, HttpContext.Current
)通过包装在另一对象的访问,并通过接口访问它。你可以这样做:
耻骨接口ISessionData
{
ExtraFieldsLinker CurrentExtraFieldsLinker {获得;组; }
}
公共类SessionDataImpl:ISessionData
{
ExtraFieldsLinker CurrentExtraFieldsLinker
{
//注:此code是有点假,
//因为我认为这些都是你的类的方法。
//但它说明了这一点。你把所有的访问这里
{返回(ExtraFieldsLinker)GetSessionObject(EXTRA_FIELDS_LINKER); }
集合{SetSessionObject(EXTRA_FIELDS_LINKER,价值); }
}
}
公共类ClassThatContainsYourAction
{
静态ClassThatContainsYourAction()
{
SessionData =新SessionDataImpl();
}
公共静态ISessionData SessionData {获得;私定; }
//使这种访问非常难看,这样你就不是偶然做
公共无效SetSessionDataForUnitTests(ISessionData sessionData)
{
SessionData = sessionData;
}
[HttpPost]
公共JsonResult GetSearchResultGrid(JqGridParams gridParams,
GUID campaignId,串queryItemsString)
{
VAR queryItems = // ...
IPageData pageData = // ...
//你的访问只能通过SessionData共享状态
VAR extraFieldLinker = SessionData.CurrentExtraFieldsLinker;
// ...
}
}
那么你的单元测试可以在调用 GetSearchResultGrid
设置 ISessionData code>实例模拟对象。
在理想情况下,你会使用一个依赖注入库在某些时候,和摆脱静态构造函数。
如果您能想出一个办法让你的 ISessionData code>实例化对象,而不是静态的,甚至更好。对象模拟框架往往喜欢创建新的模拟类型,每一个测试用例,并有嘲笑从previous测试周围地势是种毛。我相信,会话状态将是全局会话无论如何,所以你可能没有做任何事情棘手,使非静态对象的工作。
How can I unit test a method which uses a session object inside of its body?
Let us say I have the following action:
[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);
}
I made the following unit tests which fails so far because of the session thing:
[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)));
}
And the method from the helper of course:
public static ExtraFieldsLinker CurrentExtraFieldsLinker
{
get
{
object extraFieldLinker = GetSessionObject(EXTRA_FIELDS_LINKER);
return extraFieldLinker as ExtraFieldsLinker;
}
set { SetSessionObject(EXTRA_FIELDS_LINKER, value); }
}
I've solved similar issues (use of static data accessors that aren't mock friendly - in particular, HttpContext.Current
) by wrapping the access in another object, and accessing it through an interface. You could do something like:
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;
// ...
}
}
Then your unit test can set the ISessionData
instance to a mock object before calling GetSearchResultGrid
.
Ideally you'd use a Dependency Injection library at some point, and get rid of the static constructor.
If you can figure out a way to make your ISessionData
an instanced object instead of static, even better. Mock object frameworks tend to like to create a new mock type for every test case, and having mocks lying around from previous tests is kind of gross. I believe session state is going to be global to your session anyway, so you might not have to do anything tricky to make a non-static object work.
这篇关于单元测试它调用会话对象的动作,的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!