5

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

公共方法还调用了DAO查询数据库的 a 。我为班级写了一个单元测试。由于您不能为私有方法编写单元测试,因此我将所有私有方法更改为默认访问并为它们编写单元测试。

有人告诉我,我不应该仅仅为了测试目的而更改封装。但是我的公共方法调用了 DAO 并从调用中获取其数据。即使我要为 public 方法编写一个测试,我假设它会非常长和复杂。

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

任何建议将不胜感激

4

5 回答 5

9

纯粹主义者会告诉你,私有方法可以提取到另一个提供可访问方法的辅助类中,他们可能是对的。

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

于 2012-12-20T19:42:29.580 回答
7

好像你在这里有两个问题:

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

    我会看这个问题:如何测试具有私有方法、字段或内部类的类?

    我个人喜欢特朗普的回应:

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

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

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

于 2012-12-20T19:31:08.200 回答
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发送到FooDao并查看所有字段是否设置正确。您可以使用argThat捕获发送到的内容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*/ };
}
....

但是,如果您将转换分离到另一个类,如下所示:

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

您将能够测试对 LeeFooService 真正重要的内容:调用流程。Foo从到转换的测试Bar将是单元测试的责任BarToFooConverter。LeeFooService 的一个示例测试是

@RunWith(MockitoJUnitRunner.class)
public class LeFooServiceTest {
     @Mock
     private FooDao fooDao;
     @Mock
     private BarToFooConverter converter;
     @InjectMocks
     private LeeFooService service;

     @Test(expected = ConversionException.class)
     public void shouldForwardConversionException() {
         // given 
         given(converter.convert(Mockito.any(Bar.class))
            .willThrown(ConversionException.class);

         // when
         service.createAsFoo(mock(Bar.class));

         // then should have thrown exception
     }

     @Test
     public void shouldCreateConvertedFooAtDatabase() {
         // given 
         Foo convertedFoo = mock(Foo.class);
         given(converter.convert(Mockito.any(Bar.class))
            .willReturn(convertedFoo); 

         // when
         service.createAsFoo(mock(Bar.class));

         // then
         verify(fooDao).create(convertedFoo);
     }
}

希望以某种方式有所帮助:)

一些可能有用的链接:

坚硬的

BDD Mockito

于 2012-12-20T20:07:55.523 回答
5

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

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

问题是有些人认为单元测试中的“单元”是函数,而实际上是类。

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

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

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

'假设单词是 "Mike",当 CheckIfPalindronme 被调用时,它应该返回 false'

'鉴于单词是“妈妈”,当调用 CheckIfPalindronme 时,它​​应该返回 true'

'给定单词是 "",当 CheckIfPalindronme 被调用时,它应该返回 false'

'鉴于单词为空,当调用 CheckIfPalindronme 时,它​​应该抛出一个错误'

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

这是 TDD / BDD 的基础,没有这个,TDD 就不可能,因为我们必须等着看你决定如何组织你的代码,然后才能编写我们的测试。

TDD / BDD 说在你写代码之前写你的测试(顺便说一句,它工作得很好!它可以非常快速地识别需求/设计中的缺陷)

于 2017-03-22T19:40:02.133 回答
2

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

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

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

恕我直言,在以下情况下应始终考虑逻辑提取:

  • 私有方法
  • 大类(当我的编辑器窗口中出现垂直滚动条时,我通常开始想到它)
  • 循环内循环

这里的关键词是单一职责(SRP)。

于 2012-12-21T12:56:59.437 回答