简体   繁体   English

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

[英]How do I test an abstract class's protected abstract method?

I've been working on the best way to test an abstract class named TabsActionFilter . 我一直在研究测试名为TabsActionFilter的抽象类的最佳方法。 I've guranteed that classes that inherit from TabsActionFilter will have a method called GetCustomer . 我已经保证从TabsActionFilter继承的类将有一个名为GetCustomer的方法。 In practice this design seems to work well. 在实践中,这种设计似乎运作良好。

Where I've had some issues is figuring out how to test the OnActionExecuted method of the base class. 我遇到的一些问题是如何测试基类的OnActionExecuted方法。 This method relies upon the implementation of the the protected abstract GetCustomer method. 此方法依赖于受保护的抽象 GetCustomer方法的实现。 I've tried mocking the class using Rhino Mocks but can't seem to mock the return of a fake customer from GetCustomer . 我曾尝试使用Rhino Mocks嘲笑这个类,但似乎无法模仿GetCustomer假客户的回报。 Obviously, flipping the method to public will make mocking available, but protected feels like the more appropriate accessibility level . 显然,将方法转换为公共将使模拟可用,但受保护感觉更合适的可访问性级别

For the time being in my test class I've added a concrete private class that inherits from TabsActionFilter and returns a faked Customer object. 暂时在我的测试类中,我添加了一个继承自TabsActionFilter的具体私有类,并返回一个伪造的Customer对象。

  • Is a concrete class the only option? 具体类是唯一的选择吗?
  • Is there a simple mechanism of mocking that I'm missing that would allow Rhino Mocks to provide a return for GetCustomer ? 是否有一个简单的模拟机制,我错过了允许Rhino Mocks为GetCustomer提供回报?

As a note Anderson Imes discusses his opinions on this in an answer about Moq and I could be missing something key, but it doesn't seem applicable here. 作为一个注释, Anderson Imes关于Moq回答中讨论了他对此的看法,我可能会遗漏一些关键,但这似乎不适用于此。

Class that needs to be tested 需要测试的类

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);
}

Test Class and Private Class for "mocking" 用于“嘲弄”的测试类和私有类

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"
            };
        }
    }
}

I think the problem you are currently running into is that your class is not testable, or is not testable enough. 我认为您目前遇到的问题是您的课程不可测试,或者不够可测试。 This is of course assuming that you have correctly identified that GetCustomer is indeed something that needs to be mocked in order to properly test in isolation. 这当然是假设您已经正确识别出GetCustomer确实是需要模拟的东西,以便单独进行正确测试。

If GetCustomer is something that needs to be mocked in order to properly isolate and test TabsActionFilter, you will somehow need to make the implementation of GetCustomer a composable part of the class, and not an inherited method. 如果需要模拟GetCustomer以便正确隔离和测试TabsActionFilter,那么您将需要以某种方式使GetCustomer的实现成为类的可组合部分,而不是继承的方法。 The most common way to achieve this is to use Inversion of Control/Dependency Injection. 实现此目的的最常见方法是使用控制/依赖注入反转。

All that said, you COULD use something like TypeMock to achieve this. 说了这么多,你可以使用类似TypeMock来实现这一目标。 However, when you encounter a situation like this where a class is hard to test, it is usually a signal that your class has too many responsibilities and needs to be broken into smaller components. 但是,当您遇到类似难以测试的情况时,通常会发出一个信号,即您的类有太多的责任,需要将其分解为更小的组件。

(I am not a fan of using TypeMock FWIW). (我不是使用TypeMock FWIW的粉丝)。

Phil's answer is correct. 菲尔的回答是正确的。 If instead of having an abstract class, you had a class that required the injection of a customer getter (or factory, or whatever) then you'd be in good shape to test. 如果没有一个抽象类,你有一个类需要注入一个客户吸气剂(或工厂,或其他),那么你就可以进行良好的测试。 Abstract classes are the enemy of testing (and good design). 抽象类是测试(和良好的设计)的敌人。

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);
}

in your test you instantiate the now non-abstract TabsActionFilter and give it a Mock getter, which should be trivial to mock. 在你的测试中,你实例化了现在非抽象的TabsActionFilter并给它一个Mock getter,这对于mock来说应该是微不足道的。

EDIT: seems there is concern that you must have a parameterless constructor. 编辑:似乎有人担心你必须有一个无参数的构造函数。 this is easy. 这很容易。 given the above code, you then have your "real" filters implemented as 鉴于上述代码,您可以将“真实”过滤器实现为

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

you can still test your base class the same. 你仍然可以测试你的基类。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM