JUnitを使用してJavaで抽象クラスをテストする方法は?


87

JUnitを使用したJavaテストは初めてです。Javaを使用する必要があり、単体テストを使用したいと思います。

私の問題は、いくつかの抽象メソッドを持つ抽象クラスがあります。しかし、抽象的ではないいくつかの方法があります。このクラスをJUnitでテストするにはどうすればよいですか?サンプルコード(非常に単純):

abstract class Car {

    public Car(int speed, int fuel) {
        this.speed = speed;
        this.fuel = fuel;
    }

    private int speed;
    private int fuel;

    abstract void drive();

    public int getSpeed() {
        return this.speed;
    }

    public int getFuel() {
        return this.fuel;
    }
}

テストgetSpeed()してgetFuel()機能させたい。

この問題と同様の質問がここにありますが、JUnitを使用していません。

JUnitのFAQセクションでこのリンクを見つけましたが、この例で作成者が何を言いたいのかわかりません。このコード行はどういう意味ですか?

public abstract Source getSource() ;

4
Mockitoを使用した2つのソリューションについては、stackoverflow.com / questions / 1087339 /…を参照してください。
ddso 2011

テストのための別のフレームワークを学ぶ利点はありますか?MockitoはjUnitの拡張機能にすぎませんか、それともまったく別のプロジェクトですか?
vasco 2011

MockitoはJUnitを置き換えません。他のモックフレームワークと同様に、単体テストフレームワークに加えて使用され、テストケースで使用するモックオブジェクトの作成に役立ちます。
ddso 2011

回答:


104

クラスの具体的な実装がなく、メソッドがstaticそれらをテストするポイントではない場合はどうでしょうか。具象クラスがある場合は、具象クラスのパブリックAPIの一部としてそれらのメソッドをテストします。

「これらのメソッドを何度もテストしたくないので、抽象クラスを作成した理由」とあなたが考えていることは知っていますが、それに対する私の反論は、単体テストのポイントは開発者が変更できるようにすることです。テストを実行し、結果を分析します。これらの変更の一部には、との両方の抽象クラスのメソッドのオーバーライドが含まれる可能性がprotectedありpublic、その結果、基本的な動作が変更される可能性があります。これらの変更の性質によっては、アプリケーションが予期しない、場合によっては否定的な方法で実行される方法に影響を与える可能性があります。これらのタイプの変更から生じる優れた単体テストスイートの問題がある場合は、開発時に明らかになるはずです。


17
100%のコードカバレッジは神話です。アプリケーションがどのように動作するかについての既知の仮説をすべてカバーするのに十分なテストが必要です(できれば、テスト駆動開発のコードを作成する前に作成してください)。私は現在、非常に機能性の高いTDDチームに取り組んでおり、前回のビルドの時点で、開発時にすべて作成されたカバレッジは63%にすぎません。いいですか?誰が知っていますか?しかし、私は戻ってそれをより高くしようとするのは時間の無駄だと思います。
nsfyn55 2011

3
承知しました。それは良いTDDの違反であると主張する人もいます。あなたがチームに所属していると想像してみてください。メソッドが最終的なものであり、具体的な実装でテストを行わないことを前提としています。誰かが修飾子を削除し、継承階層のブランチ全体に波及する変更を加えます。テストスイートでそれをキャッチしてみませんか?
nsfyn55 2013年

31
同意しません。TDDで作業するかどうかに関係なく、抽象クラスの具象メソッドにはコードが含まれているため、(サブクラスがあるかどうかに関係なく)テストを行う必要があります。さらに、Javaでのユニットテストは(通常)クラスをテストします。したがって、クラスの一部ではなく、そのスーパークラスの一部であるメソッドのテストには、実際にはロジックはありません。そのロジックに従って、サブクラスがまったくないクラスを除いて、Javaでクラスをテストしないでください。オーバーライドされるメソッドに関しては、それはまさに、サブクラスのテストへの変更/追加をチェックするためのテストを追加するときです。
ethanfar 2014

3
@ nsfyn55具体的な方法があったとしfinalたら?実装変更できない場合、同じメソッドを複数回テストする理由がわかりません
ダイオキシン2015

3
すべての実装でテストを実行できるように、テストで抽象インターフェイスをターゲットにするべきではありませんか?それが不可能な場合は、私たちが知り、修正したいリスコフの原則に違反することになります。実装が拡張された(互換性のある)機能を追加する場合にのみ、それに対して(そしてその追加機能のためだけに)特定の単体テストを行う必要があります。
2016

36

抽象クラスを継承する具象クラスを作成し、具象クラスが抽象クラスから継承する関数をテストします。


抽象クラスを拡張する10個の具象クラスがあり、これらの具象クラスのそれぞれが1つのメソッドのみを実装し、他の2つのメソッドが抽象で実装されているため、これらのクラスのそれぞれで同じである場合はどうしますかクラス?私の場合、抽象クラスのテストをコピーして各サブクラスに貼り付けたくないということです。
スカーフェイス

12

あなたが投稿したサンプルクラスでは、テストすることはあまり意味がないようでgetFuel()ありgetSpeed()、0を返すことしかできないためです(セッターはありません)。

ただし、これは説明のための単純化された例であり、抽象基本クラスのメソッドをテストする正当な理由があると仮定すると(他の人はすでにその影響を指摘しています)、匿名を作成するようにテストコードを設定できます抽象メソッドのダミー(no-op)実装を提供するだけの基本クラスのサブクラス。

たとえば、あなたTestCaseはこれを行うことができます:

c = new Car() {
       void drive() { };
   };

次に、残りのメソッドをテストします。例:

public class CarTest extends TestCase
{
    private Car c;

    public void setUp()
    {
        c = new Car() {
            void drive() { };
        };
    }

    public void testGetFuel() 
    {
        assertEquals(c.getFuel(), 0);
    }

    [...]
}

(この例はJUnit3構文に基づいています。JUnit4の場合、コードは少し異なりますが、考え方は同じです。)


ご回答ありがとうございます。はい、私の例は単純化されています(そしてあまり良くありません)。ここですべての答えを読んだ後、私はダミークラスを書きました。しかし、彼の答えに@ nsfyn55と書いているように、私はこの抽象クラスのすべての子孫に対してテストを書きます。
vasco 2011

9

とにかく解決策が必要な場合(たとえば、抽象クラスの実装が多すぎて、テストが常に同じ手順を繰り返すため)、その実装によって実行される抽象ファクトリメソッドを使用して抽象テストクラスを作成できます。テストクラス。この例は、TestNGで機能します。

の抽象テストクラスCar

abstract class CarTest {

// the factory method
abstract Car createCar(int speed, int fuel);

// all test methods need to make use of the factory method to create the instance of a car
@Test
public void testGetSpeed() {
    Car car = createCar(33, 44);
    assertEquals(car.getSpeed(), 33);
    ...

の実装 Car

class ElectricCar extends Car {

    private final int batteryCapacity;

    public ElectricCar(int speed, int fuel, int batteryCapacity) {
        super(speed, fuel);
        this.batteryCapacity = batteryCapacity;
    }

    ...

クラスElectricCarTestのユニットテストクラスElectricCar

class ElectricCarTest extends CarTest {

    // implementation of the abstract factory method
    Car createCar(int speed, int fuel) {
        return new ElectricCar(speed, fuel, 0);
    }

    // here you cann add specific test methods
    ...

5

あなたはこのようなことをすることができます

public abstract MyAbstractClass {

    @Autowire
    private MyMock myMock;        

    protected String sayHello() {
            return myMock.getHello() + ", " + getName();
    }

    public abstract String getName();
}

// this is your JUnit test
public class MyAbstractClassTest extends MyAbstractClass {

    @Mock
    private MyMock myMock;

    @InjectMocks
    private MyAbstractClass thiz = this;

    private String myName = null;

    @Override
    public String getName() {
        return myName;
    }

    @Test
    public void testSayHello() {
        myName = "Johnny"
        when(myMock.getHello()).thenReturn("Hello");
        String result = sayHello();
        assertEquals("Hello, Johnny", result);
    }
}

4

抽象クラスから継承するjUnit内部クラスを作成します。これはインスタンス化でき、抽象クラスで定義されているすべてのメソッドにアクセスできます。

public class AbstractClassTest {
   public void testMethod() {
   ...
   }
}


class ConcreteClass extends AbstractClass {

}

3
これは素晴らしいアドバイスです。ただし、例を提供することで改善できます。おそらくあなたが説明しているクラスの例です。
SDJMcHattie 2014年

2

匿名クラスをインスタンス化してから、そのクラスをテストできます。

public class ClassUnderTest_Test {

    private ClassUnderTest classUnderTest;

    private MyDependencyService myDependencyService;

    @Before
    public void setUp() throws Exception {
        this.myDependencyService = new MyDependencyService();
        this.classUnderTest = getInstance();
    }

    private ClassUnderTest getInstance() {
        return new ClassUnderTest() {    
            private ClassUnderTest init(
                    MyDependencyService myDependencyService
            ) {
                this.myDependencyService = myDependencyService;
                return this;
            }

            @Override
            protected void myMethodToTest() {
                return super.myMethodToTest();
            }
        }.init(myDependencyService);
    }
}

可視性は抽象クラスのprotectedプロパティに対するものでなければならないことに注意してください。myDependencyServiceClassUnderTest

このアプローチをMockitoとうまく組み合わせることができます。こちらをご覧ください


2

これをテストする私の方法は、それぞれの中で非常に簡単abstractUnitTest.javaです。抽象クラスを拡張するクラスをabstractUnitTest.javaに作成するだけです。そして、そのようにテストします。


0

抽象クラス全体をテストすることはできません。この場合、抽象メソッドがあります。これは、指定された抽象クラスを拡張するクラスによって実装する必要があることを意味します。

そのクラスでは、プログラマーは自分のロジック専用のソースコードを作成する必要があります。

言い換えれば、抽象クラスの最終的な動作を確認できないため、抽象クラスをテストする感覚はありません。

一部の抽象クラスに抽象メソッドに関連しない主要な機能がある場合は、抽象メソッドが例外をスローする別のクラスを作成するだけです。


0

オプションとして、抽象クラス内のロジックをカバーする抽象テストクラスを作成し、サブクラステストごとに拡張できます。このようにして、このロジックが各子に対して個別にテストされることを確認できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.