如何对扩展/继承第 3 方类的类进行单元测试

Posted

技术标签:

【中文标题】如何对扩展/继承第 3 方类的类进行单元测试【英文标题】:How to unit test a class which extends/inherits a 3rd party class 【发布时间】:2012-06-14 05:30:36 【问题描述】:

我创建了一个扩展第 3 方抽象类的新类。新类调用抽象类中的方法。我遇到的问题是在尝试编写单元测试时,我不确定如何编写测试,因为我不知道 3rd 方类所需的确切细节。

下面的 AbstractDecoratorMapper 是 SiteMesh 特定的类,我必须扩展它才能使 SiteMesh 正常工作。据我从文档中可以看出,我不能使用组合。

public final class PartnerDecoratorMapper extends AbstractDecoratorMapper 

    @Override
    public void init(Config config, Properties properties, DecoratorMapper parent) throws InstantiationException 
        super.init(config, properties, parent);
    

    @Override
    public Decorator getDecorator(HttpServletRequest request, Page page) 
        if (super.getDecorator(request, page).getName().equalsIgnoreCase("default")) 
            return getNamedDecorator(request, "externalPartnerDefault");
        
        return super.getDecorator(request, page);
    

如果这个工具可以提供任何帮助,我会使用 JMock。

【问题讨论】:

【参考方案1】:

您不需要了解 3rd 方库的详细信息...您只需要模拟它们返回您期望从它们那里得到的东西。

如果您继承它,您可以设置该类,因为它将在生产中使用并调用应该在常规使用中调用的方法。您需要检查的只是结果是否符合您的预期。 (您不需要确切知道第 3 方课程在测试中的工作方式)。

为了检查您实现的所有代码是否都已执行,您可以使用插件来检查覆盖率,例如 eclemma - http://www.eclemma.org/)

【讨论】:

在我的测试中,我需要使用 3 个参数调用 init() 方法,但我真的不知道它们是什么,因为 SiteMesh 会为每个页面请求创建它们。所以在我的测试中,我是否仍然需要创建这些参数,直到测试成功,所以这只是一个反复试验的问题? 如果你只是想测试你的一段代码,你可以用一些可接受的参数来实例化它并在你的测试中覆盖(实现下降你的类特定于你的测试,你将覆盖一些方法应该返回一个期望值)你的代码所依赖的方法。【参考方案2】:

使用 JMock,据我所知,您无法模拟对被测类的超类的调用。

this related question 的答案可能有用,但主要答案是切换到支持此功能的模拟框架(建议使用JMockit)。

【讨论】:

【参考方案3】:

您可能会问两件事。我会尽量回答。

可能的问题 1) 我如何测试我的附加功能?

可能的问题 2) 我如何测试组合的功能是否合适?

让我从问题 1 开始。

我会建议一个简单的果皮。这里有一个技术视频:http://www.youtube.com/watch?v=p0tILwRZH5Q (视频是c#但在java中基本相同)

这意味着你之后会有以下代码

@Override
public Decorator getDecorator(HttpServletRequest request, Page page) 
    Decorator d = super.getDecorator(request, page);
    return getResolvedDecorator(d, d.getName(), request);


public Decorator getResolvedDecorator(Decorator current, String name, HttpServletRequest request) 
    if (name.equalsIgnoreCase("default")) 
        return getNamedDecorator(request, "externalPartnerDefault");
    
    return current;

现在你可以通过调用来测试它

assertEquals(expected, new PartnerDecoratorMapper().getResolvedDecorator(null, "default", MockHttpServletRequest);

我建议也可以从 HttpServletRequest 中删除数据,以使测试和意图更清晰。

注意:这具有额外的性能优势,即对 super.getDecorator() 的调用只发生一次,因为结果被缓存了。

附加说明:还值得注意的是,init() 的覆盖是不必要的,实际上并没有做任何事情。

问题 2:这是一个更难的问题,因为您没有说明期望的行为是什么。 我假设这是一种工厂模式,所以很可能它会以某种方式工作,例如

| http请求质量1 |页面质量 1|预期装饰者 | | /mypath/mypage |状态 = 富 |我的装饰器 |

(我不知道上面的图表应该包含什么或看起来像什么) 一旦你有这种行为,那么测试以确保它会相当简单。

快乐测试, 卢埃林

【讨论】:

感谢您花时间回答这个问题。我犯了一个小错误,但在您的新方法 (getResolvedDecorator) 中,对 getNamedDecorator() 的调用将是 super.getNamedDecorator()。这仍然会改变您的实现,因为这会调用 3rd 方扩展类? 不客气。答案取决于设置和调用 getNamedDecorator() 的难度。如果这很难,我倾向于希望能够避免它,但总的来说,您仍然希望关注给定的页面和 url 请求特征 a、b、c 期望一个 d 类型的装饰器。 并且通过一些重构,您的测试应该看起来像这两行【参考方案4】:

因为我不知道第 3 方班级需要的确切细节

任何类型的测试都会比较实际做什么和它应该做什么。在您知道应该做什么之前,您无法进行测试:“第 3 部分课程需要什么”。

如果你不知道基类需要什么,你怎么能做出任何合理的尝试来编写你的派生类呢?您要扩展的类的 API 文档是怎么说的?

问题可能是没有这样的 API 文档,还是没有提供信息?可能是,虽然该类不是final,但它并不是真正打算用作基类。其中任何一个都表明 API 设计不佳,建议您不要费心使用它

还是您不知道确切细节的关键困难?这可能是您方法中的一个缺陷:通常您不能也绝不能对确切细节做出假设。您的代码应符合文档化 API,并且您不应假设文档化 API 中未说明的任何内容。这可能意味着您无法知道何时调用模板方法或将传递给它们的值是什么。如果基类通过传递声明为实现interface 或非final 类(即它们的类型而不是它们的类 已指定),您无法知道这些对象将具有什么类。

【讨论】:

以上是关于如何对扩展/继承第 3 方类的类进行单元测试的主要内容,如果未能解决你的问题,请参考以下文章

我应该对扩展 Sonata Base EntityManager 类的类进行单元测试吗?

iphone OCMockObject 和单元测试从 NSURLConnection 继承的类

扩展特征的单元测试类 - 我如何在特征中模拟和存根方法?

如何对第一个Vue.js组件进行单元测试 (上)

如何在 Swift 中对私有或内部函数进行单元测试?

如何对具有依赖项的工厂进行单元测试