1

単体テストについて基本的な質問があります。コードにクラッシュが見つかったので、バグを再現するテストを作成しました。次に、バグを修正し、テストに合格することを確認しました。私の質問は、私が最善のアプローチをとったかどうかです。簡単な例を次に示します。

public class ClassC
{
    private int internalFlag;

    void all(Dependency dep1, Dependency dep2, Dependency dep3, Dependency dep4)
    {
        a(dep1);
        b(dep2)
        c(dep3)
        d(dep4)
        e();
        f();
    }

    void e()
    {
        if (some logic based on dep3)
        {
            internalFlag = 2;
        }
    }

    void f()
    {
        if (internalFlag == 2)
        {
            Log("All is well");
        }
        else
        {
            Log("Crash occurs")
        }
    }
}

上記の例では、メソッド a、b、c、d、e、f を呼び出す「all」というメソッドがあります。ここで、「internalVar」が期待値ではなかったため、「f」でクラッシュが見つかりました。この 'internalVar' は "e" で設定されましたが、条件が false の場合、"e" は internalFalg を何も設定しません。したがって、バグは e にあります。

バグを切り分けるテストを書くことができました。その後、バグを修正したところ、テストが過去のものであることがわかりました。それは素晴らしいことですが、それを行うには、次のようなことをしなければなりませんでした:

void testAll()
{
    mockDep1 = mock(dep1);
    mockDep2 = mock(dep2);
    mockDep3 = mock(dep3);
    mockDep4 = mock(dep4);

    all(mockDep1, mockDep2, mockDep3, mockDep4);
}

この例では、mockDep1..4 を作成する部分は非常に単純ですが、実際には非常に長い手間のかかるコードでした。私の質問は、代わりに最小限の呼び出しを実行してクラッシュを再現することは有効ですか?

void testCrash()
{
   mockDep3 = mock(dep3);
   c(mockDep3)    
   e()
   f();
}

これは、クラッシュを再現してテストするために必要なすべてのことですが、各メソッドを呼び出す実際のメソッド「all」でコードがどのように呼び出されるかは正確ではありません。バグごとに何度も何度もすべてのモックを含めて上記の testAll を記述するのは少し面倒なようです。しかし、繰り返しますが、それが実際のメソッド「all」がどのように機能するかです。完全なメソッドが呼び出された場合に分離が正確に機能しない場合でも、代わりにバグを分離する方が良いでしょうか?

4

1 に答える 1

0

あなたの例から明らかでないのは、どのメソッドがパブリックで、どのメソッドがプライベート(または保護またはパッケージプライベート)であるかです。唯一の public メソッドが と呼ばれるものである場合all、メソッドの 1 つだけに実際に含まれる複雑なロジックをテストする場合を除き、可能な限りこれをテストで呼び出す必要があります。

どのテストを作成するかを選択するときは、このクラスのコントラクトが何であるかに焦点を当てる必要があります。アプリケーションの残りの部分は、このクラスにどのような機能を提供することを期待していますか? その機能が何であるかに基づいてテスト メソッドを作成します。あなたがたまたま書いたメソッドではありません。

さらに、Dependency クラスの唯一の目的が ClassC の機能をサポートすることである場合は、SUT がこの ClassC と Dependency を一緒にしたテストを作成することを検討できます (これらを「統合テスト」と呼ぶ人もいれば、「単体テスト」と呼ぶ人もいます)。ここで、単位は 2 つのクラスで構成されます」)。

あなたの特定のケースでは、クラスがメソッドへの分割全体で実行される機能を提供しているようです。したがって、機能をテストし、機能の動作が異なる可能性があるさまざまなシナリオをテストします。個々のメソッドをテストしないでください。all特に、さまざまなシナリオでメソッドを呼び出す多数のテストがあります。エラーの発生につながるもの (少なくともバグを修正する前) もあれば、そうでないものもあります。

最後に、テスト機能とシナリオの観点から考えるのを助けるために、呼び出されているメソッドではなく、実行されている機能とシナリオに従ってテスト メソッドに名前を付ける必要があると思います。特に、testAll(実際には、後にメソッド名がtest続く名前) は、テスト メソッドとしては不適切な名前であると考えています。しかし、それは別の日に考えるべきことかもしれません。

于 2012-09-27T22:25:41.553 に答える