web-dev-qa-db-ja.com

モックJava列挙型で値を追加して失敗した場合をテストする

私はenumスイッチを多かれ少なかれこのように持っています:

public static enum MyEnum {A, B}

public int foo(MyEnum value) {
    switch(value) {
        case(A): return calculateSomething();
        case(B): return calculateSomethingElse();
    }
    throw new IllegalArgumentException("Do not know how to handle " + value);
}

そして、すべての行をテストでカバーしたいのですが、コードはすべての可能性に対処することが期待されているため、対応するcaseステートメントがスイッチになければ値を提供できません。

列挙型を拡張して追加の値を追加することはできません。また、equalsメソッドをモックしてfalseを返すだけでも機能しません。 。だから、PowerMockか何かで黒魔術を達成できると思った。

ありがとう!

編集

列挙型を所有しているので、値にメソッドを追加するだけでスイッチの問題を完全に回避できると考えました。しかし、まだ興味深いので質問を残しています。

48
fortran

完全な例を次に示します。

コードは元のコードとほとんど同じです(テストの検証を簡略化しただけです):

public enum MyEnum {A, B}

public class Bar {

    public int foo(MyEnum value) {
        switch (value) {
            case A: return 1;
            case B: return 2;
        }
        throw new IllegalArgumentException("Do not know how to handle " + value);
    }
}

そして、ここに完全なコードカバレッジの単体テストがあります。テストはPowermock(1.4.10)、Mockito(1.8.5)、およびJUnit(4.8.2)で動作します。

@RunWith(PowerMockRunner.class)
public class BarTest {

    private Bar bar;

    @Before
    public void createBar() {
        bar = new Bar();
    }

    @Test(expected = IllegalArgumentException.class)
    @PrepareForTest(MyEnum.class)
    public void unknownValueShouldThrowException() throws Exception {
        MyEnum C = PowerMockito.mock(MyEnum.class);
        Whitebox.setInternalState(C, "name", "C");
        Whitebox.setInternalState(C, "ordinal", 2);

        PowerMockito.mockStatic(MyEnum.class);
        PowerMockito.when(MyEnum.values()).thenReturn(new MyEnum[]{MyEnum.A, MyEnum.B, C});

        bar.foo(C);
    }

    @Test
    public void AShouldReturn1() {
        assertEquals(1, bar.foo(MyEnum.A));
    }

    @Test
    public void BShouldReturn2() {
        assertEquals(2, bar.foo(MyEnum.B));
    }
}

結果:

Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.628 sec
47
Jonny Heggheim

@メロウェア

... switch()ステートメントを実行するコードJavaはJava.lang.ArrayIndexOutOfBoundsをスローします...

これと同じ問題があります。テストクラスで最初に新しいEnumを使用してテストを実行します。この問題でバグを作成しました: https://code.google.com/p/powermock/issues/detail?id=44

6

編集で示したように、enum自体に機能を追加できます。ただし、これは「One Responsibility」の原則に違反する可能性があるため、最良の選択肢ではないかもしれません。これを実現する別の方法は、キーとして列挙値を、値として機能を含む静的マップを作成することです。この方法では、すべての値をループすることにより、列挙値に有効な動作があるかどうかを簡単にテストできます。この例では少し手に入らないかもしれませんが、これはresource idsenumの値にマッピングするためによく使うテクニックです。

2
bert bruynooghe

fooの最後の行にテストをヒットさせるために、いくつかの根本的なバイトコード操作を使用するのではなく、代わりに静的コード分析に依存します。たとえば、IntelliJ IDEAには「Enum switchステートメントが大文字と小文字を区別しない」コード検査があり、fooメソッドがcase

2
Rogério

jMock(少なくとも私が使用しているバージョン2.5.1の時点では)はこれをすぐに実行できます。 ClassImposterizerを使用するには、Mockeryを設定する必要があります。

Mockery mockery = new Mockery();
mockery.setImposterizer(ClassImposterizer.INSTANCE);
MyEnum unexpectedValue = mockery.mock(MyEnum.class);
1
Kevin Peterson

まず、Mockitoは整数長などのモックデータを作成できます。enumには特定の数の序数名の値などがあるため、正しい列挙型を作成できません。

public enum HttpMethod {
      GET, POST, PUT, DELETE, HEAD, PATCH;
}

だから私は列挙型HttpMethodに合計5つの序数を持っていますが、mockitoはそれを知りません。Mockitoは常にモックデータとそのnullを作成し、null値を渡すことになります。したがって、ここでは、順序をランダム化し、他のテストに渡すことができる正しい列挙型を取得する提案されたソリューションがあります

import static org.mockito.Mockito.mock;

import Java.util.Random;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Matchers;
import org.mockito.internal.util.reflection.Whitebox;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import com.amazonaws.HttpMethod;




//@Test(expected = {"LoadableBuilderTestGroup"})
//@RunWith(PowerMockRunner.class)
public class testjava {
   // private static final Class HttpMethod.getClass() = null;
    private HttpMethod mockEnumerable;

    @Test
    public void setUpallpossible_value_of_enum () {
        for ( int i=0 ;i<10;i++){
            String name;
            mockEnumerable=    Matchers.any(HttpMethod.class);
            if(mockEnumerable!= null){
                System.out.println(mockEnumerable.ordinal());
                System.out.println(mockEnumerable.name());

                System.out.println(mockEnumerable.name()+"mocking suceess");
            }
            else {
                //Randomize all possible  value of  enum 
                Random Rand = new Random();
                int ordinal = Rand.nextInt(HttpMethod.values().length); 
                // 0-9. mockEnumerable=
                mockEnumerable= HttpMethod.values()[ordinal];
                System.out.println(mockEnumerable.ordinal());
                System.out.println(mockEnumerable.name());
            }
        }
    }







    @Test
    public void setUpallpossible_value_of_enumwithintany () {
        for ( int i=0 ;i<10;i++){
            String name;
            mockEnumerable=    Matchers.any(HttpMethod.class);
            if(mockEnumerable!= null){
                System.out.println(mockEnumerable.ordinal());
                System.out.println(mockEnumerable.name());

                System.out.println(mockEnumerable.name()+"mocking suceess");
            } else {
               int ordinal;
               //Randomize all possible  value of  enum 
               Random Rand = new Random();
               int imatch =  Matchers.anyInt();
               if(  imatch>HttpMethod.values().length)
                 ordinal = 0    ;
               else
                ordinal = Rand.nextInt(HttpMethod.values().length);

               // 0-9.  mockEnumerable=
               mockEnumerable= HttpMethod.values()[ordinal];
               System.out.println(mockEnumerable.ordinal());
               System.out.println(mockEnumerable.name());       
            }
       }  
    }
}

出力:

0
GET
0
GET
5
PATCH
5
PATCH
4
HEAD
5
PATCH
3
DELETE
0
GET
4
HEAD
2
PUT
0
Jin Thakur

IllegalArgumentExceptionに到達する最も簡単な方法は、fooメソッドにnullを渡すことだと思います。「nullの処理方法がわからない」

0
Steven Kuypers