2012-06-22 38 views
7

Tôi viết các bài kiểm tra JUnit cho một số Bộ điều khiển MVC Spring. Việc khởi tạo kiểm tra JUnit là phổ biến cho tất cả các kiểm tra Bộ điều khiển của tôi, vì vậy tôi muốn tạo một lớp trừu tượng để thực hiện việc khởi tạo này.Làm thế nào để có được lớp của một trường kiểu T?

Vì vậy, tôi đã tạo ra đoạn mã sau:

@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); 
    } 

} 

Ý tưởng là để tạo ra, đối với mỗi bộ điều khiển Tôi muốn thử nghiệm một lớp học thử nghiệm JUnit kéo dài AbstractSpringMVCControllerTest tôi. Loại được đưa ra trong tuyên bố extends là lớp của Bộ điều khiển.

Ví dụ, nếu tôi muốn kiểm tra tôi AccountController, tôi sẽ tạo ra lớp AccountControllerTest như thế:

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); 
     ... 
    } 

} 

Vấn đề của tôi nằm ở dòng cuối cùng của phương pháp initContext() của lớp trừu tượng. Lớp trừu tượng này khai báo đối tượng controller làm đối tượng T, nhưng làm cách nào để có thể nói đến ngữ cảnh ứng dụng mùa xuân trả lại bean loại T?

Tôi đã thử một cái gì đó như thế:

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

nhưng controllerClass trả về lớp java.lang.Object.class, không AccountController.class.

Tất nhiên, tôi có thể tạo phương thức public abstract Class<?> getControllerClass();, sẽ bị xóa bởi từng lớp kiểm tra JUnit Controller, nhưng tôi muốn tránh giải pháp này.

Vì vậy, có ý tưởng nào không?

+0

Nếu bạn có ý tưởng tốt hơn cho tiêu đề câu hỏi, đừng ngần ngại :) – romaintaz

+1

Chào mừng bạn đến với loại cơn ác mộng xóa được loại java. –

+2

Không thể. Bản sao của [Nhận tên loại cho tham số chung của lớp chung chung] (http://stackoverflow.com/questions/6624113/get-type-name-for-generic-parameter-of-generic-class) –

Trả lời

4

Điều này có thể nếu các lớp con của bạn là AbstractSpringMVCControllerTest liên kết T lúc biên dịch. Nghĩa là, bạn có cái gì đó như

public class DerpControllerTest extends AbstractSpringMVCControllerTest<DerpController> { } 

hơn

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

Tôi đoán bạn có thể có các cựu. Trong trường hợp này, các loại T sẽ bị xóa khỏi đối tượng Class cho AbstractSpringMVCControllerTest lúc chạy, nhưng đối tượng Class cho DerpControllerTestkhông cung cấp một cách để biết T là gì, vì nó bị ràng buộc T tại thời gian biên dịch.

Các lớp sau đây chứng minh làm thế nào để truy cập vào loại 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> { 
} 

Test.java

public class Test { 
    public static void main(String...args) { 
     Sub s = new Sub(); 
     System.out.println(s.getObjectType()); // prints "Class java.lang.Double" 
    } 
} 
+0

Tốt, tôi không nhận ra bạn có thể nhận được các thông số chung của lớp như thế này. Ngay cả khi lớp kiểm tra không trực tiếp phân lớp lớp cha mẹ chung, tôi chắc chắn bạn có thể viết một số mã để đi tới phân cấp lớp và nhận được ở lớp hoặc giao diện mà bạn đang theo dõi. – Alex

+0

Vâng, điều đó chắc chắn có thể, nhưng tôi không muốn nhận được vào đó, bởi vì xử lý 'Sub mở rộng Super ; SubSub mở rộng Sub 'sẽ khác với' Sub mở rộng Super ; SubSub mở rộng Sub'. Và điều đó vẫn không giải quyết được khả năng giới thiệu các thông số chung khác, như 'Sub mở rộng Siêu ; SubSub mở rộng Sub '. – matts

+0

Cảm ơn, nó đã hoạt động! – romaintaz

1

Bạn không thể vì thời gian chạy, do ERASURE, JVM không thể biết lớp thuộc tính "bộ điều khiển" của bạn. Nó được coi là đối tượng ...

3

Điều này khác với loại tẩy xóa mà chúng ta thường thấy. Với kiểu xóa, chúng ta không biết tham số của lớp hiện tại (lớp bạn nhận được với getClass()), nhưng bạn có thể nhận được thông số này trong giao diện siêu hạng/siêu (những thứ bạn nhận được với getGenericSuperxxxxx()) vì đây là một phần của khai báo kiểu .

Điều này sẽ không cung cấp cho bạn loại trường controller, nhưng tôi hy vọng điều này là đủ cho mục đích của bạn.

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 

Trong trường hợp của bạn, nó sẽ là

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

cái gì đó để ghi chú:

Nếu lớp B là al được tham số hóa như thế này:

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

Điều này sẽ không hoạt động. Hoặc nếu bạn có một lớp mở rộng B, nó sẽ có vấn đề quá. Tôi sẽ không đi sâu vào tất cả những trường hợp đó, nhưng bạn nên có ý tưởng.

+0

Con người, sự phản chiếu thật tuyệt vời, nhưng tôi ghét cách Java thực hiện nó. Đoán đó là những gì xảy ra khi bạn thực hiện sự phản chiếu cho một ngôn ngữ không hỗ trợ ban đầu và không được thiết kế với sự phản ánh trong tâm trí. ... Sau đó, một lần nữa có một vài điều tôi không thích về cách thư viện chuẩn của Java được thiết lập, do đó, đó có thể là vấn đề để bắt đầu. – JAB

Các vấn đề liên quan