私は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か何かで黒魔術を達成できると思った。
ありがとう!
編集:
列挙型を所有しているので、値にメソッドを追加するだけでスイッチの問題を完全に回避できると考えました。しかし、まだ興味深いので質問を残しています。
完全な例を次に示します。
コードは元のコードとほとんど同じです(テストの検証を簡略化しただけです):
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
@メロウェア
... switch()ステートメントを実行するコードJavaはJava.lang.ArrayIndexOutOfBoundsをスローします...
これと同じ問題があります。テストクラスで最初に新しいEnumを使用してテストを実行します。この問題でバグを作成しました: https://code.google.com/p/powermock/issues/detail?id=44
編集で示したように、enum自体に機能を追加できます。ただし、これは「One Responsibility」の原則に違反する可能性があるため、最良の選択肢ではないかもしれません。これを実現する別の方法は、キーとして列挙値を、値として機能を含む静的マップを作成することです。この方法では、すべての値をループすることにより、列挙値に有効な動作があるかどうかを簡単にテストできます。この例では少し手に入らないかもしれませんが、これはresource idsをenumの値にマッピングするためによく使うテクニックです。
foo
の最後の行にテストをヒットさせるために、いくつかの根本的なバイトコード操作を使用するのではなく、代わりに静的コード分析に依存します。たとえば、IntelliJ IDEAには「Enum switch
ステートメントが大文字と小文字を区別しない」コード検査があり、foo
メソッドがcase
。
jMock(少なくとも私が使用しているバージョン2.5.1の時点では)はこれをすぐに実行できます。 ClassImposterizerを使用するには、Mockeryを設定する必要があります。
Mockery mockery = new Mockery();
mockery.setImposterizer(ClassImposterizer.INSTANCE);
MyEnum unexpectedValue = mockery.mock(MyEnum.class);
まず、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
IllegalArgumentExceptionに到達する最も簡単な方法は、fooメソッドにnullを渡すことだと思います。「nullの処理方法がわからない」