web-dev-qa-db-ja.com

Javaでローカル内部クラスメソッドをテストするにはどうすればよいですか?

多くのアプリケーションでは、専用のサブアルゴリズム(または単に明確に定義されたコード)を利用するアルゴリズムを使用することがよくあります。

これまで、メインアルゴリズムを作成したときに、以下の例(OldStyle)のように、サブアルゴリズムごとにプライベートメソッドを作成しました。

public class OldStyle {

    public int mainAlg() {
        int x = subAlg01();
        int y = subAlg02();
        int z = x * y;
        return z;
    }

    private int subAlg01() {
        return 3;
    }

    private int subAlg02() {
        return 5;
    }
}

これは問題なく機能しましたが、プライベートであっても1つのメソッド(mainAlg)でのみ使用されるメソッド(subAlg01とsubAlg02)が急増するのは好きではありませんでした。

最近、ローカル内部クラスの使用を発見しました。今、私の例は(NewStyle)です。

public class NewStyle {

    public int mainAlg() {
        class Nested {

            public int subAlg01() {
                return 3;
            }

            public int subAlg02() {
                return 5;
            }
        }
        Nested n = new Nested();
        int x = n.subAlg01();
        int y = n.subAlg02();
        int z = x * y;
        return z;
    }
}

私はそれがとても好きですが、今私は次の問題を抱えています:JUnitを使用してsubAlg01とsubAlg02をテストするにはどうすればよいですか?

ちなみに、私はEclipseを使用しています。

助けてくれてありがとう。

編集:もっとよく説明しようとしています:たとえば、並べ替えアルゴリズムがあり、期待どおりに実行されることを確認するためにテストしたいと思います。このソートアルゴリズムは、クラスXのメソッドmでのみ使用されます。クラスXのプライベートメソッドにすることはできますが、クラスXは通常、ソートとは関係ありません。なぜ、クラスXをソートメソッドで「台無しにする」のでしょうか。だから私はそれをメソッドmの中に入れました。しばらくして、並べ替えアルゴリズムを改善したい(高速化したい)が、動作が期待どおりであることを確認したいので、元のテストで再テストしたい。

それが私がやりたいことです。解決策がないかもしれません。誰かが私を助けてくれることを願っています。

回答の選択後に編集。ロドニーの答えを選んだのは、彼の解決策が私が採用したものだからです。スタンドアロンのヘルパークラスは、サブメソッドが何であるかを明確に把握するのに役立ち(ヘルパーです!)、それらをテストする機能も提供します。

23
Filippo Tabusso

フィリッポ、私はあなたの問題といくつかの答えに対する欲求不満を理解しています。何年も前に初めてJUnitを使い始めたとき、私もプライベートコードをテストしたかったので、それが悪い考えだと言うのは馬鹿げていると思いました。

彼らは正しかったことがわかりました(驚きです!)が、かなりの数のテストを書いた後で初めて、私は理解しましたなぜ。同じプロセスを経る必要があるかもしれませんが、最終的には同じ結論に達します;-)

とにかく、あなたの状況では、私はNestedを適切なスタンドアロンクラスにし、おそらくそれがヘルパークラスであることを明らかにするために別のパッケージにします。次に、他のテストとは関係なく、直接テストを作成します。

次に、NewStyleのテストを作成し、NewStyleの動作のみに焦点を当てます。

(おそらく、Nested内でインスタンス化するのではなくNewStyleNewStyleに挿入します。つまり、NewStyleのコンストラクターへの引数にします。

次に、テストでNewStyleのテストを作成するときに、Nestedのインスタンスに合格して続行します。特に注意が必要な場合は、Nestedからインターフェイスを作成し、2番目の実装を作成し、それを使用してNewStyleもテストします。)

13
Rodney Gitzel

クラスのパブリックインターフェイスのみをテストする必要があり、プライベートメンバーやプライベート内部クラスはテストしないでください。プライベートメンバーは、実装の詳細であり、クラスのパブリックメソッドによってのみ(直接的または間接的に)使用されます。したがって、呼び出し元のメソッドを介して間接的にこれらを単体テストできます。これらの単体テストで十分な粒度がないと感じた場合、または関心のある結果(の一部)を感知できない場合、これはおそらく設計に問題があることを示しています。クラスが大きすぎる可能性があります。やりすぎると、その機能の一部を別のクラスに抽出する必要があり、そこで直接単体テストを行うことができます。

現在の例では、内部クラス自体に多くのコードが含まれている場合は、それをトップレベルクラスに変換するだけで、そのメソッドを直接単体テストできます。

(ところで、それが囲んでいるクラスインスタンスを参照する必要がない場合、内部クラスはstaticである必要があります。)

20
Péter Török

実際には、ローカルの内部クラスをテストできます。ただし、インターフェイスを実装する必要があります。このようにして、リフレクションを介してローカルクラスのインスタンスを作成し、それをそのインターフェイスにキャストできます。インターフェイスタイプを取得したら、問題なく実装コードをテストできます。

インターフェース:

public interface Algorithm {
    int subAlg01();
    int subAlg02();
}

クラス:

public class NewStyle {
    public int mainAlg() {
        class Nested implements Algorithm {

            public int subAlg01() {
                return 3;
            }

            public int subAlg02() {
                return 5;
            }
        }
        Nested n = new Nested();
        int x = n.subAlg01();
        int y = n.subAlg02();
        int z = x * y;
        return z;
    }
}

テスト:

public class NewStyleTest {

    @Test
    public void testLocal() throws ClassNotFoundException,
            NoSuchMethodException, SecurityException, InstantiationException,
            IllegalAccessException, IllegalArgumentException,
            InvocationTargetException {
        Class<?> forName = Class.forName("NewStyle$1Nested");
        Constructor<?> declaredConstructor = forName
                .getDeclaredConstructor(NewStyle.class);
        declaredConstructor.setAccessible(true);
        Algorithm algorithm = (Algorithm) declaredConstructor
                .newInstance(new NewStyle());

        assertEquals(algorithm.subAlg01(), 3);
        assertEquals(algorithm.subAlg02(), 5);
    }
}
1
Kai Sternad

これらのクラスには外部から到達できないため、junitはそれらをテストできません。それらをテストするには、公開されているものが必要です。

理由だけでコードを複雑にしすぎないようにする必要があります

メソッドが急増するのは好きではありません

あなたの場合、メソッドが外部クラスのメソッドであるかどうか、または内部クラスがどうしても必要な場合は、mainAlg()メソッドの外部に配置して、グローバルに表示できるようにすることができます。

_public class NewStyle {

    public int mainAlg() {

        Nested n = new Nested();
        int x = n.subAlg01();
        int y = n.subAlg02();

        int z = x * y;
        return z;
    }

    class Nested {

        public int subAlg01() {
            return 3;

        }

        public int subAlg02() {
            return 5;
        }
    }
}
_

これは、new NewStyle().new Nested().subAlg01();を使用して呼び出すことができます。

1
Kingo

この問題は、内部クラスの動作がメソッドの動作のコア部分である場合に発生します。メソッドがあるとすると、実行可能オブジェクトを3番目のオブジェクトに渡すことになっています。これは、実際にはそうではない内部クラスです。別のエンティティ:この場合、適切な単体テストでは、3番目のオブジェクトをモックして内部クラスを実行し、引数キャプチャを使用してテストする必要があります。

この種の問題は、関数型プログラミングをより多くサポートしている言語で非常に一般的であり、サードパーティに渡されたラムダのテストがほとんどの要件です。

ただし、前に述べたように、内部クラスが外部の関係者によって使用されない場合、それは単なる実装の詳細であり、個別にテストすることは役に立ちません。

1
bob

うーん、Groovyのような言語はJavaで単体テストを行うためによく使用され、プライベートフィールドとメソッドに透過的にアクセスできることを知っています.....しかし、ネストされたクラスについてはよくわかりません。なぜこのようなことをしたいかは理解できますが、ゲッターとセッターのテストと同じ立場をとっています。パブリックメソッドをテストすることの副作用としてテストする必要があります。そうでない場合は、そのようにテストされたら、なぜ彼らはそもそもそこにいるのですか?

1
mezmo