2012-02-29 11 views
2

現在、Spring MVCベースのWebアプリケーションを作成しています。Mockito/Spring MVC - (アノテーション駆動型)リクエストマッピングの動的検証

注釈付きメソッドごとに1つのテストを書くのではなく、Parameterized JUnitランナーの恩恵を受けたいと思います。

最後に、私は私のコントローラメソッドで自分のラッパー対応へのすべてのプリミティブの引数を変更する(し、手動でヌルレフリーの健全性チェックを行う)しなければならなかったが、それはほとんど、働いて得ました。次のカスタムクラスMethodArgumentと

@RunWith(Parameterized.class) 
public class MyControllerMappingTest { 

    private MockHttpServletRequest request; 
    private MockHttpServletResponse response; 
    private MyController mockedController; 
    private AnnotationMethodHandlerAdapter annotationHandlerAdapter; 
    private final String httpMethod; 
    private final String uri; 
    private final String controllerMethod; 
    private final Class<?>[] parameterTypes; 
    private final Object[] parameterValues; 

    @Before 
    public void setup() { 
     request = new MockHttpServletRequest(); 
     response = new MockHttpServletResponse(); 
     mockedController = mock(MyController.class); 
     annotationHandlerAdapter = new AnnotationMethodHandlerAdapter(); 
    } 

    @Parameters 
    public static Collection<Object[]> requestMappings() { 
     return asList(new Object[][] { 
       {"GET", "/my/uri/0", "index", arguments(new MethodArgument(Integer.class, 0))} 
     }); 
    } 

    private static List<MethodArgument> arguments(MethodArgument... arguments) { 
     return asList(arguments); 
    } 

    public MyControllerMappingTest(String httpMethod, String uri, String controllerMethod, List<MethodArgument> additionalParameters) { 
     this.httpMethod = httpMethod; 
     this.uri = uri; 
     this.controllerMethod = controllerMethod; 
     this.parameterTypes = new Class<?>[additionalParameters.size()]; 
     initializeParameterTypes(additionalParameters); 
     this.parameterValues = newArrayList(transform(additionalParameters, valueExtractor())).toArray(); 
} 

    private void initializeParameterTypes(List<MethodArgument> additionalParameters) { 
     Iterable<Class<?>> classes = transform(additionalParameters, typeExtractor()); 
     int i = 0; 
     for (Class<?> parameterClass : classes) { 
      parameterTypes[i++] = parameterClass; 
     } 
    } 

    @Test 
    public void when_matching_mapping_constraints_then_controller_method_automatically_called() throws Exception { 
     request.setMethod(httpMethod); 
     request.setRequestURI(uri); 
     annotationHandlerAdapter.handle(request, response, mockedController); 

     Method method = MyController.class.getMethod(controllerMethod, parameterTypes); 
     method.invoke(verify(mockedController), parameterValues); 
    } 
} 

それは助けることができる場合は、ここでのコード(これはまた、グアバに依存)である

public class MethodArgument { 
    private final Class<?> type; 
    private final Object value; 

    public MethodArgument(final Class<?> type, final Object value) { 
     this.type = type; 
     this.value = value; 
    } 

    public Object getValue() { 
     return value; 
    } 

    public Class<?> getType() { 
     return type; 
    } 

    public static Function<MethodArgument, Class<?>> typeExtractor() { 
     return new Function<MethodArgument, Class<?>>() { 
      @Override 
      public Class<?> apply(MethodArgument argument) { 
       return argument.getType(); 
      } 
     }; 
    } 

    public static Function<MethodArgument, Object> valueExtractor() { 
     return new Function<MethodArgument, Object>() { 
      @Override 
      public Object apply(MethodArgument argument) { 
       return argument.getValue(); 
      } 
     }; 
    } 
} 

をので、私はほとんどそこです、ここでの唯一のテストケースはJava Integerキャッシュのために機能し、したがってIntegerインスタンスは呼び出しチェーン全体で同じです...これはカスタムオブジェクトでは機能しませんが、私はいつもInvocationTargetExceptionで終わります(原因: "Argument (複数可)は違います!」)...

タイプは正しいですが、渡されたインスタンスは@Parametersメソッドで設定されたものと同じではありません。

これを回避する方法はありますか?

答えて

2

はあなたの馬をホールド!

SpringSourceの春試験-MVCモジュールを焼いている: https://github.com/SpringSource/spring-test-mvc

+0

私はPiwai(私は彼にIRL ^^を知っている)で説明したように、私の最初のアプローチは間違っている...与えられたHTTPリクエストがルーティングされていることをテストします特定の方法への実装の問題です... したがって、私が欲しいものを機能的にテストする最も適切な方法は、実際の要求を送信/シミュレートし、返された応答内容を確認することです。簡単に言えば、これは私が正しい方向に向いているので、この答えを受け入れるでしょう:) – Rolf

+0

そして私はあなたを認識しませんでした-_- ' こんにちは! :) – Rolf

1

動作するサンプルを提供するのではなく、提供しないサンプルを提供してスタックトレースを提供するといいでしょう。

私はすぐにGoogleをチェックしましたが、Mockitoはうまく処理していないようですreflection on spy objects

実際にこの道を歩きたいのであれば、反射データを提供するのではなく、そこからモックを実際に呼び出して、呼び出されたメソッドをパラメータ化されたデータの一部として提供する別の方法があります。

私は手で任意のIDEなしていることを書いているので、そこにコンパイルエラーかもしれませんが、あなたのアイデアを得るでしょう:JavaのLambasが、これは、より読みやすく役立つだろう持つ、もちろん

@RunWith(Parameterized.class) 
public class MyControllerMappingTest { 

    public interface VerifyCall<T> { 
     void on(T controller); 
    } 

    @Parameters 
    public static Collection<Object[]> requestMappings() { 
     Object[][] testCases = {  
      {"GET", "/my/uri/0", new VerifyCall<MyController>() { 
       @Override 
       public void on(MyController controller) { 
        controller.index(0); 
       } 
      }} 
     }; 
     return asList(testCases); 
    } 

    private MockHttpServletRequest request; 
    private MockHttpServletResponse response; 
    private MyController mockedController; 
    private AnnotationMethodHandlerAdapter annotationHandlerAdapter; 

    private final String httpMethod; 
    private final String uri; 
    private final VerifyCall<MyController> verifyCall; 

    public MyControllerMappingTest(String httpMethod, String uri, VerifyCall<MyController> verifyCall) { 
     this.httpMethod = httpMethod; 
     this.uri = uri; 
     this.verifyCall = verifyCall; 
    } 

    @Before 
    public void setup() { 
     request = new MockHttpServletRequest(); 
     response = new MockHttpServletResponse(); 
     mockedController = mock(MyController.class); 
     annotationHandlerAdapter = new AnnotationMethodHandlerAdapter(); 
    } 

    @Test 
    public void when_matching_mapping_constraints_then_controller_method_automatically_called() throws Exception { 
     request.setMethod(httpMethod); 
     request.setRequestURI(uri); 
     annotationHandlerAdapter.handle(request, response, mockedController); 

     verifyCall.on(verify(mockedController)); 
    } 
} 

を。

ます。またFunkyJFunctionalを使用することができます。

@RunWith(Parameterized.class) 
public class MyControllerMappingTest { 

    @Parameters 
    public static Collection<Object[]> requestMappings() { 
     class IndexZero extends FF<MyController, Void> {{ in.index(0); }} 
     Object[][] testCases = { //   
       {"GET", "/my/uri/0", withF(IndexZero.clas)} 

     }; 
     return asList(testCases); 
    } 

    private MockHttpServletRequest request; 
    private MockHttpServletResponse response; 
    private MyController mockedController; 
    private AnnotationMethodHandlerAdapter annotationHandlerAdapter; 

    private final String httpMethod; 
    private final String uri; 
    private final Function<MyController, Void> verifyCall; 

    public MyControllerMappingTest(String httpMethod, String uri, Function<MyController, Void> verifyCall) { 
     this.httpMethod = httpMethod; 
     this.uri = uri; 
     this.verifyCall = verifyCall; 
    } 

    @Before 
    public void setup() { 
     request = new MockHttpServletRequest(); 
     response = new MockHttpServletResponse(); 
     mockedController = mock(MyController.class); 
     annotationHandlerAdapter = new AnnotationMethodHandlerAdapter(); 
    } 

    @Test 
    public void when_matching_mapping_constraints_then_controller_method_automatically_called() throws Exception { 
     request.setMethod(httpMethod); 
     request.setRequestURI(uri); 
     annotationHandlerAdapter.handle(request, response, mockedController); 

     verifyCall.apply(verify(mockedController)); 
    } 
} 

いくつかのサイドノート:

  • を読みやすさのために、それはあなたのクラスで最初にあなたの静的メンバを置くことをお勧めです。インスタンスメソッド(setup())もコンストラクタの後に置かなければなりません。私はこの構文は読みやすくするために見つける

    return asList(new Object[][] { 
        {}, 
        {} 
    }; 
    

  • 配列構文:

代わりにこの構文の

Object[][] testCases = { 
    {}, 
    {} 
}; 
return asList(testCases); 
関連する問題