用于单元测试的适当方法封装

时间:2012-12-20 19:14:10

标签: java unit-testing methods junit encapsulation

我的课程包含14 private methods1 public method。 public方法通过其他私有方法直接或间接调用所有私有方法。

public方法还调用查询数据库的DAO。 我为班级写了一个单元测试。由于您无法为私有方法编写单元测试,因此我将所有私有方法更改为默认访问权限并为其编写单元测试。

有人告诉我,我不应仅仅为了测试而更改封装。但我的公共方法调用DAO并从调用中获取其数据。即使我要为公共方法编写测试,我也认为它会非常复杂。

我应该如何解决这个问题。一方面,我必须为访问DAO的公共方法编写一个非常复杂的测试,另一方面,更改方法的访问级别并为它们编写简短的测试方法。我该怎么办?

任何建议都将不胜感激

5 个答案:

答案 0 :(得分:9)

纯粹主义者会告诉你私有方法可以被提取到另一个提供可访问方法的助手类,它们可能是正确的。

但是如果将这些实用程序方法保留在类中是有意义的,如果该类不是公共API的一部分并且不打算进行子类化(例如,它可能是最终的),我看不到任何使一些私有方法受到包保护或保护的问题。特别是如果记录了这种非私人可见性,例如使用Guava注释@VisibleForTesting

答案 1 :(得分:7)

好像你有两个问题:

  1. 如何测试私有方法(在Java中假设):

    我会看看这个问题:How do I test a class that has private methods, fields or inner classes?

    我个人喜欢Trumpi's回复:

      

    测试私有方法的最佳方法是通过另一种公共方法。如果无法做到这一点,则满足下列条件之一:

         
        
          
    1. 私有方法是死代码
    2.     
    3. 您正在测试的课程附近有设计气味
    4.     
    5. 您尝试测试的方法不应该是私有的
    6.        
  2. 如何打破DAO的依赖

    您可以尝试使用Dependency Injection来摆脱对DAO的依赖。然后你可以模拟DAO并将其注入你的测试用例。 好处是它真正成为单元测试,而不是集成测试。

答案 2 :(得分:6)

如果它很复杂,可能是因为你的班级有多个责任。通常,当你有私有方法做不同的事情时,你可以使用公共方法为你做不同的类。您的课程将变得更容易阅读,测试,您将分离责任。 14种私人方法通常表示这种事情:P

例如,您可能会有类似

的内容
public class LeFooService {
    private final OtherServiceForConversion barService;
    private final FooDao fooDao;

    public LeeFooService(FooDao dao, OtherServiceForConversion barService) {
        this.barService = barService;
        this.fooDao = dao;
    }

    public void createAsFoo(Bar bar) throws ConversionException {
        Foo foo = convert(bar);

        fooDao.create(foo);
    }

    private Foo convert(Bar bar) {
        // lots of conversion stuff, services calling D:
    }
}

为了正确测试,您必须测试转换是否正确完成。由于它是私密的,因此您必须捕获发送到foo的{​​{1}}并查看是否所有字段都已正确设置。您可以使用argThat来捕获发送到FooDao的内容以测试转换。您的测试看起来像

fooDao

但是,如果你将转换分成另一个类,就像这样:

....
@Test
public void shouldHaveConvertedFooCorrectly() {
     // given
     Bar bar = mock(Bar.class);

     // when
     fooService.createAsFoo(bar);

     // then
     verify(fooDao).create(argThat(fooIsConvertedCorrectly());
}

private ArgumentMatcher<Foo> fooIsConvertedCorrectly() {
     return new ArgumentMatcher<Foo>() { /*test stuff*/ };
}
....

您将能够测试对LeeFooService非常重要的内容:呼叫流程。从public class LeFooService { private final BarToFooConverter bar2FooConverter; private final FooDao fooDao; public LeeFooService(FooDao dao, BarToFooConverter bar2FooConverter) { this.bar2FooConverter = bar2FooConverter; this.fooDao = dao; } public void createAsFoo(Bar bar) throws ConversionException { Foo foo = bar2FooConverter.convert(bar); fooDao.create(foo); } } Foo的转换测试将是Bar的单元测试的责任。 LeeFooService的示例测试将是

BarToFooConverter

希望以某种方式帮助:)

一些可能有用的链接:

SOLID

BDD Mockito

答案 3 :(得分:3)

如父母会告诉他们的孩子:不要泄露你的私隐!

您不需要公开您的私有方法来测试它们。您可以获得100%PERCENT测试覆盖率,包括那些私有方法,而不会暴露它们。

有些人认为&#39;单位&#39;单元测试是功能,当它真的是一个类。

例如:我有一个带有1个公共方法的类:   bool CheckIfPalindrome(string wordToCheck)。

在内部,我有私有方法来验证wordToCheck的长度,如果它是空的,如果它是空的,那么bla bla bla。

但作为测试人员,我不需要知道或关心开发人员如何组织(或组织)内部代码。我正在测试界面的实现。

&#39;鉴于&#34; Mike&#34;,当调用CheckIfPalindronme时,它应该返回false&#39;

&#39;鉴于&#34;妈妈&#34;,当调用CheckIfPalindronme时,它应该返回true&#39;

&#39;如果单词是&#34;&#34;,当调用CheckIfPalindronme时,它应该返回false&#39;

&#39;如果单词为null,当调用CheckIfPalindronme时,它应该抛出错误&#39;

如果我涵盖所有可能的输入和预期输出,我将测试您的私人功能。

这是TDD / BDD的基础,如果没有这个,TDD是不可能的,因为我们必须等待,看看你在我们编写测试之前是如何决定组织代码的。

TDD / BDD表示在编写代码之前编写测试(它的工作原理很棒!它很快就能识别出需求/设计中的缺陷)

答案 4 :(得分:2)

包含一个公共方法和14个私有方法的类几乎无法测试。没有看到它,我愿意打赌,它是非常不固定的。就像JB Nizet所说的那样;我和我的纯粹主义同事会提取大部分或全部私人方法来帮助班级。原因是:

  • 易于测试
  • 易于重构
  • 易于阅读
  • 易于重复使用

不提取的原因: * 很多!!课程 *提取需要时间 *有时,性能问题阻碍了“正确”设计的美感。

在以下情况下,应始终考虑IMHO提取逻辑:

  • 私人方法
  • 大班(我通常会在什么时候开始考虑它 垂直滚动条出现在我的编辑器winow中)
  • 在其中循环 环

此处的关键字是单一责任(SRP)。