我一直在研究测试名为TabsActionFilter
的抽象类的最佳方法。我保证从TabsActionFilter
继承的类将有一个名为GetCustomer
的方法。在实践中,这种设计似乎运作良好。
我遇到的一些问题是如何测试基类的OnActionExecuted
方法。此方法依赖于 protected abstract GetCustomer
方法的实现。我曾尝试使用Rhino Mocks来模拟该类,但似乎无法模仿来自GetCustomer
的虚假客户的回复。显然,将方法转移到 public 会使模拟可用,但 protected 感觉更合适accessibility level。
暂时在我的测试类中,我添加了一个继承自TabsActionFilter
的具体私有类,并返回一个伪造的Customer对象。
GetCustomer
提供回报?请注意Anderson Imes在answer about Moq中讨论他对此的看法,我可能会遗漏一些关键内容,但这似乎并不合适。
需要测试的类
public abstract class TabsActionFilter : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
Customer customer = GetCustomer(filterContext);
List<TabItem> tabItems = new List<TabItem>();
tabItems.Add(CreateTab(customer, "Summary", "Details", "Customer",
filterContext));
tabItems.Add(CreateTab(customer, "Computers", "Index", "Machine",
filterContext));
tabItems.Add(CreateTab(customer, "Accounts", "AccountList",
"Customer", filterContext));
tabItems.Add(CreateTab(customer, "Actions Required", "Details",
"Customer", filterContext));
filterContext.Controller.ViewData.PageTitleSet(customer.CustMailName);
filterContext.Controller.ViewData.TabItemListSet(tabItems);
}
protected abstract Customer GetCustomer(ActionExecutedContext filterContext);
}
“嘲笑”的测试类和私有类
public class TabsActionFilterTest
{
[TestMethod]
public void CanCreateTabs()
{
// arrange
var filterContext = GetFilterContext(); //method omitted for brevity
TabsActionFilterTestClass tabsActionFilter =
new TabsActionFilterTestClass();
// act
tabsActionFilter.OnActionExecuted(filterContext);
// assert
Assert.IsTrue(filterContext.Controller.ViewData
.TabItemListGet().Count > 0);
}
private class TabsActionFilterTestClass : TabsActionFilter
{
protected override Customer GetCustomer(
ActionExecutedContext filterContext)
{
return new Customer
{
Id = "4242",
CustMailName = "Hal"
};
}
}
}
答案 0 :(得分:2)
我认为您目前遇到的问题是您的课程不可测试,或者不够可测试。这当然是假设您已经正确识别出GetCustomer确实需要被模拟,以便能够单独进行正确测试。
如果为了正确隔离和测试TabsActionFilter而需要模拟GetCustomer,那么您将需要以某种方式使GetCustomer的实现成为类的可组合部分,而不是继承的方法。实现此目的的最常见方法是使用控制/依赖注入反转。
所有这一切,你 COULD 使用类似TypeMock的东西来实现这一目标。但是,当你遇到这样一种难以测试的类的情况时,通常会发出一个信号,表明你的类有太多的责任,需要分成更小的组件。
(我不喜欢使用TypeMock FWIW)。
答案 1 :(得分:1)
public class TabsActionFilter : ActionFilterAttribute
{
private readonly ICustomerGetter _getter;
public TabsActionFilter(ICustomerGetter getter)
{ _getter = getter; }
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
Customer customer = _getter.GetCustomer(filterContext);
...
}
}
public interface ICustomerGetter
{
Customer GetCustomer(ActionExecutedContext filterContext);
}
在你的测试中你实例化了现在非抽象的TabsActionFilter并给它一个Mock getter,这对于模拟应该是微不足道的。
编辑:似乎有人担心你必须有一个无参数构造函数。这很简单。鉴于上述代码,您可以将“真实”过滤器实现为
public class MyFilter : TabsActionFilter
{
public MyFilter() : base(new MyGetter()) {}
}
你仍然可以测试你的基类。