5

私のクラスには14 private methodsとが含まれてい1 public methodます。パブリック メソッドは、すべてのプライベート メソッドを直接または他のプライベート メソッドを介して間接的に呼び出します。

public メソッドにはDAO、データベースを照会する への呼び出しもあります。クラスの単体テストを作成しました。プライベート メソッドの単体テストを記述できないため、すべてのプライベート メソッドをデフォルト アクセスに変更し、それらの単体テストを記述しました。

テストのためだけにカプセル化を変更するべきではないと言われました。しかし、私のパブリック メソッドには DAO への呼び出しがあり、呼び出しからデータを取得します。public メソッドのテストを書いたとしても、それは非常に長く複雑なものになると思います。

この問題にどのようにアプローチすればよいでしょうか。一方では、DAO にアクセスする public メソッドの非常に複雑なテストを作成する必要があり、他方では、メソッドのアクセス レベルを変更して、それらの短い単純なテスト メソッドを作成する必要があります。私は何をすべきか?

どんな提案でも大歓迎です

4

5 に答える 5

9

純粋主義者は、プライベートメソッドがアクセス可能なメソッドを提供する別のヘルパークラスに抽出される可能性があり、それらが正しい可能性があることを教えてくれます。

しかし、これらのユーティリティメソッドをクラス内に保持することが理にかなっている場合、クラスがパブリックAPIの一部ではなく、サブクラス化されることを意図していない場合(たとえば、finalである可能性があります)、作成に問題はありません。そのプライベートメソッドのいくつかは、パッケージ保護または保護されています。特に、この非プライベートの可視性が、たとえばGuavaアノテーションで文書化されている場合@VisibleForTesting

于 2012-12-20T19:42:29.580 に答える
7

ここには2つの問題があるようです:

  1. プライベート メソッドをテストする方法 (Java を想定):

    この質問を見てみましょう:プライベート メソッド、フィールド、または内部クラスを持つクラスをテストするにはどうすればよいですか?

    私は個人的にTrumpiの反応が好きです:

    プライベート メソッドをテストする最良の方法は、別のパブリック メソッドを使用することです。これができない場合は、次のいずれかの条件が当てはまります。

    1. プライベート メソッドはデッド コードです
    2. テストしているクラスの近くにデザインの匂いがあります
    3. テストしようとしているメソッドはプライベートであってはなりません
  2. DAO の依存関係を解消する方法

    Dependency Injectionを使用して、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 モッキート

于 2012-12-20T20:07:55.523 に答える
2

1 つのパブリック メソッドと 14 のプライベート メソッドを含むクラスをテストすることはほぼ不可能です。それを見たことがなければ、私はそれが非常に堅実ではないことに賭けても構わないと思います. JB Nizetが言うように; 私と私の純粋主義者の同僚は、ほとんどまたはすべてのプライベート メソッドをヘルパー クラスに抽出していました。理由は次のとおりです。

  • テストが簡単
  • リファクタリングが容易
  • 読みやすい
  • 再利用が容易

抽出しない理由: * たくさん!! クラスのクラス * 抽出に時間がかかる * パフォーマンスの問題が「適切な」デザインの美しさを妨げることがあります。

ロジックの IMHO 抽出は、次の場合に常に考慮する必要があります。

  • プライベート メソッド
  • 大きなクラス (通常、エディター ウィンドウに垂直スクロールバーが表示されたときに考え始めます)
  • ループ内のループ

ここでのキーワードは、Single Responsibility (SRP) です。

于 2012-12-21T12:56:59.437 に答える