7

いくつかの Spring MVC コントローラーの JUnit テストを作成します。JUnit テストの初期化は、すべてのコントローラー テストに共通しているため、この初期化を行う抽象クラスを作成したいと考えました。

したがって、次のコードを作成しました。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath*:spring/applicationContext-test.xml", "classpath*:spring/spring-mvc-test.xml" })
@Transactional
public abstract class AbstractSpringMVCControllerTest<T> {

    @Autowired
    protected ApplicationContext applicationContext;

    protected MockHttpServletRequest request;

    protected MockHttpServletResponse response;

    protected HandlerAdapter handlerAdapter;

    protected T controller;

    @SuppressWarnings("unchecked")
    @Before
    public void initContext() throws SecurityException, NoSuchFieldException {
        request = new MockHttpServletRequest();
        response = new MockHttpServletResponse();
        handlerAdapter = applicationContext.getBean(AnnotationMethodHandlerAdapter.class);
        // Does not work, the problem is here...    
        controller = applicationContext.getBean(T);
    }

}

アイデアは、テストしたいコントローラごとに、 my を拡張する JUnit テスト クラスを作成することですAbstractSpringMVCControllerTestextends宣言で指定された型はControllerのクラスです。

たとえば、自分の をテストしたい場合は、次のようなクラスAccountControllerを作成します。AccountControllerTest

public class AccountControllerTest extends AbstractSpringMVCControllerTest<AccountController> {

    @Test
    public void list_accounts() throws Exception {
        request.setRequestURI("/account/list.html");
        ModelAndView mav = handlerAdapter.handle(request, response, controller);
        ...
    }

}

initContext()私の問題は、抽象クラスのメソッドの最後の行にあります。この抽象クラスはcontrollerオブジェクトをオブジェクトとして宣言しますTが、どのようにして Spring Application Context にタイプ の Bean を返すように指示できますTか?

私はそのようなことを試しました:

    Class<?> controllerClass = this.getClass().getSuperclass().getDeclaredField("controller").getType();
    controller = (T) applicationContext.getBean(controllerClass);

ではなく、クラスをcontrollerClass返します。java.lang.Object.classAccountController.class

もちろん、public abstract Class<?> getControllerClass();各 JUnit コントローラー テスト クラスによってオーバーライドされるメソッドを作成することはできますが、この解決策は避けたいと思います。

それで、何か考えはありますか?

4

3 に答える 3

4

これは、コンパイル時にサブクラスがバインドされている場合に可能です。つまり、次のようなものがありますAbstractSpringMVCControllerTestT

public class DerpControllerTest extends AbstractSpringMVCControllerTest<DerpController> { }

それよりも

public class AnyControllerTest<T> extends AbstractSpringMVCControllerTest<T> { }

あなたはおそらく前者を持っていると思います。この場合、 の型は実行時にオブジェクト forTから消去されますが、オブジェクト forコンパイル時にバインドされるため、何が何であるかを知る方法を提供します。ClassAbstractSpringMVCControllerTestClassDerpControllerTest TT

次のクラスは、 の型にアクセスする方法を示していますT

Super.java

import java.lang.reflect.ParameterizedType;
public abstract class Super<T> {

    protected T object;

    @SuppressWarnings("unchecked")
    public Class<T> getObjectType() {
        // This only works if the subclass directly subclasses this class
        return (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

}

Sub.java

public class Sub extends Super<Double> {
}

テスト.java

public class Test {
    public static void main(String...args) {
        Sub s = new Sub();
        System.out.println(s.getObjectType()); // prints "Class java.lang.Double"
    }
}
于 2012-06-22T15:28:07.500 に答える
3

This is different from the type erasure we normally see. With type erasure, we don't know the parameter of the current class (the one you get with getClass()), but you can get those in super class / super interface (those you get with getGenericSuperxxxxx()) because this is part of the type declaration.

This won't give your the type of controller field, but I hope this is enough for your purpose.

Code:

public class A<P> {
}

import java.lang.reflect.ParameterizedType;

public class B extends A<String> {

    public  static void main(String[] arg) {
        System.out.println(
                ((ParameterizedType)B.class.getGenericSuperclass()).getActualTypeArguments()[0]);
    }
}

Output:

class java.lang.String

In your case, it would be

Class controllerClass = (Class)( ((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[0]);

Something to notes:

If the class B is also parameterized like this:

public class B<X> extends A<X> {}

This won't work. Or if you have another class extends B, it will have problem too. I won't go into all those cases, but you should get the idea.

于 2012-06-22T15:25:02.027 に答える
1

ERASURE により、実行時に JVM が「コントローラー」属性のクラスを認識できないため、実行できません。オブジェクトと見なされます...

于 2012-06-22T15:15:05.067 に答える