如何测试抽象类的受保护抽象方法?

我一直在研究测试名为TabsActionFilter的抽象类的最佳方法。 我已经保证从TabsActionFilterinheritance的类将有一个名为GetCustomer的方法。 在实践中,这种设计似乎运作良好。

我遇到的一些问题是如何测试基类的OnActionExecuted方法。 此方法依赖于受保护的抽象 GetCustomer方法的实现。 我曾尝试使用Rhino Mocks嘲笑这个类,但似乎无法模仿GetCustomer假客户的回报。 显然,将方法翻转为公共将使模拟可用,但受保护感觉更合适的可访问性级别 。

暂时在我的测试类中,我添加了一个inheritance自TabsActionFilter的具体私有类,并返回一个伪造的Customer对象。

  • 具体类是唯一的选择吗?
  • 是否有一个简单的模拟机制,我错过了允许Rhino Mocks为GetCustomer提供回报?

作为一个注释, Anderson Imes在关于Moq的回答中讨论了他对此的看法,我可能会遗漏一些关键,但这似乎不适用于此。

需要测试的类

 public abstract class TabsActionFilter : ActionFilterAttribute { public override void OnActionExecuted(ActionExecutedContext filterContext) { Customer customer = GetCustomer(filterContext); List tabItems = new List(); 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" }; } } } 

我认为您目前遇到的问题是您的课程不可测试,或者不够可测试。 这当然是假设您已经正确识别出GetCustomer确实需要被模拟以便单独进行正确测试。

如果需要模拟GetCustomer以正确隔离和测试TabsActionFilter,那么您将需要以某种方式使GetCustomer的实现成为类的可组合部分,而不是inheritance的方法。 实现此目的的最常见方法是使用控制/dependency injection反转。

总而言之,您可以使用TypeMock之类的东西来实现这一目标。 但是,当您遇到这样的情况时,类很难测试,这通常是一个信号,表明您的类有太多的责任,需要分解成更小的组件。

(我不喜欢使用TypeMock FWIW)。

菲尔的回答是正确的。 如果不是有一个抽象类,你有一个类需要注入一个客户吸气剂(或工厂,或其他),那么你将处于良好的状态进行测试。 抽象类是测试(和良好的设计)的敌人。

 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,这对于mock来说应该是微不足道的。

编辑:似乎有人担心你必须有一个无参数的构造函数。 这很简单。 鉴于上述代码,您可以将“真实”filter实现为

 public class MyFilter : TabsActionFilter { public MyFilter() : base(new MyGetter()) {} } 

你仍然可以测试你的基类。