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

Sus*_*sie 5 java methods junit unit-testing encapsulation

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

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

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

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

任何建议将不胜感激

JB *_*zet 9

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

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


sli*_*ive 7

好像你有两个问题:

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

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

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

    测试私有方法的最佳方法是通过另一种公共方法.如果无法执行此操作,则满足下列条件之一:

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

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


Cae*_*alf 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:
    }
}
Run Code Online (Sandbox Code Playgroud)

为了正确测试,您必须测试转换是否正确完成.因为它是私有的,所以你必须捕获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*/ };
}
....
Run Code Online (Sandbox Code Playgroud)

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

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

您将能够测试对LeeFooService非常重要的内容:呼叫流程.转换的从测试FooBar将从单元测试的责任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);
     }
}
Run Code Online (Sandbox Code Playgroud)

希望有所帮助:)

一些可能有用的链接:

固体

BDD Mockito


Mik*_*Emo 5

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

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

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

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

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

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

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

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

'鉴于单词是 "",当调用 CheckIfPalindronme 时,它​​应该返回 false'

'给定这个词是空的,当调用 CheckIfPalindronme 时,它​​应该抛出一个错误'

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

这是 TDD / BDD 的基础,没有这个,TDD 就不可能实现,因为在我们编写测试之前,我们将不得不等待,看看您是如何决定组织代码的。

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