2014-04-18 14 views
20

mô phỏng tối thiểu tuyệt đối nào phải được thực hiện để vượt qua bài kiểm tra này?PowerMock: giả lập biến cố định tĩnh cuối cùng, một ví dụ cụ thể

mã:

class PrivateStaticFinal { 
    private static final Integer variable = 0; 
    public static Integer method() { return variable + 1; } 
} 

kiểm tra:

@RunWith(PowerMockRunner.class) 
@PrepareForTest(PrivateStaticFinal.class) 
class PrivateStaticFinalTest { 
    @Test 
    public void testMethod() { 
     //TODO PrivateStaticFinal.variable = 100 
     assertEquals(PrivateStaticFinal.method(), 101); 
    } 
} 

liên quan: Mock private static final variables in the testing class (không trả lời rõ ràng)

+1

Các thống kê này đều nằm trong cùng một lớp khiến cho việc này trở nên phức tạp. Tất cả các vấn đề tôi gặp phải là sử dụng các số liệu thống kê từ những nơi khác khi cố gắng kiểm tra một lớp học. Có để giả một phần các phần của lớp bạn đang cố gắng để thử nghiệm chỉ có vẻ xấu. Bạn không bao giờ nên chế giễu lớp bạn đang thử nghiệm, chỉ là những thứ mà nó phụ thuộc vào. – Walls

Trả lời

34

Disclaimer: Sau rất nhiều săn bắn xung quanh trên chủ đề khác nhau tôi đã tìm thấy một câu trả lời. Nó có thể được thực hiện, nhưng sự đồng chung là nó không phải là rất an toàn nhưng nhìn thấy như thế nào bạn đang làm điều này ONLY IN kiểm tra đơn vị, tôi nghĩ rằng bạn chấp nhận những rủi ro :)


Câu trả lời là không Mocking, vì hầu hết Mocking không cho phép bạn đột nhập vào trận chung kết. Câu trả lời là một chút "hacky", nơi bạn đang thực sự sửa đổi các lĩnh vực tư nhân khi Java được gọi là lõi java.lang.reflect.Fieldjava.lang.reflect.Modifier lớp học (phản ánh). Nhìn vào this answer Tôi đã có thể ghép lại phần còn lại của bài kiểm tra của bạn mà không cần chế giễu giải quyết vấn đề của bạn.

Vấn đề với câu trả lời đó là tôi đã chạy vào NoSuchFieldException khi cố gắng sửa đổi variable. Trợ giúp cho việc đó nằm trong another post về cách truy cập một trường riêng tư và không công khai.

Reflection/Dòng Manipulation Giải thích:

Kể từ Mocking không thể xử lý cuối cùng, thay vì những gì chúng ta cuối cùng làm là hack vào thư mục gốc của lĩnh vực riêng của mình. Khi chúng ta sử dụng các thao tác Field (phản chiếu), chúng ta đang tìm biến cụ thể bên trong một lớp/đối tượng. Một khi Java tìm thấy nó, chúng ta có được "modifier" của nó, cho biết biến giới hạn/quy tắc nó có như là final, static, private, public, v.v. Chúng tôi tìm biến đúng, và sau đó cho biết mã có thể truy cập cho phép chúng tôi thay đổi các công cụ sửa đổi này. Khi chúng tôi đã thay đổi "quyền truy cập" ở gốc để cho phép chúng tôi thao tác, chúng tôi sẽ chuyển sang phần "cuối cùng" của nó. Sau đó chúng ta có thể thay đổi giá trị và đặt nó thành bất cứ thứ gì chúng ta cần. Để đơn giản, chúng tôi đang sửa đổi biến để cho phép chúng tôi thay đổi các thuộc tính của nó, loại bỏ nguyên mẫu cho final và sau đó thay đổi giá trị vì nó không còn là final nữa. Để biết thêm thông tin về điều này, check out the post where the idea came from.

Vì vậy, từng bước chúng tôi vượt qua trong biến chúng ta muốn thao tác và ...

// Mark the field as public so we can toy with it 
field.setAccessible(true); 
// Get the Modifiers for the Fields 
Field modifiersField = Field.class.getDeclaredField("modifiers"); 
// Allow us to change the modifiers 
modifiersField.setAccessible(true); 
// Remove final modifier from field by blanking out the bit that says "FINAL" in the Modifiers 
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); 
// Set new value 
field.set(null, newValue); 

Kết hợp này tất cả vào một ĐÁP SUPER mới bạn nhận được.

@RunWith(PowerMockRunner.class) 
@PrepareForTest() 
class PrivateStaticFinalTest { 
    @Test 
    public void testMethod(){ 
     try { 
     setFinalStatic(PrivateStaticFinal.class.getDeclaredField("variable"), Integer.valueOf(100)); 
     } 
     catch (SecurityException e) {fail();} 
     catch (NoSuchFieldException e) {fail();} 
     catch (Exception e) {fail();} 
     assertEquals(PrivateStaticFinal.method(), Integer.valueOf(101)); 
    } 

    static void setFinalStatic(Field field, Object newValue) throws Exception { 
     field.setAccessible(true); 
     // remove final modifier from field 
     Field modifiersField = Field.class.getDeclaredField("modifiers"); 
     modifiersField.setAccessible(true); 
     modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); 
     field.set(null, newValue); 
    } 
} 

Cập nhật Các giải pháp trên sẽ làm việc chỉ dành cho những hằng số đó được khởi tạo trong tĩnh block.When tuyên bố và khởi tạo hằng số cùng một lúc, nó có thể xảy ra rằng inlines biên dịch nó, lúc này bất kỳ thay đổi nào đối với giá trị ban đầu đều bị bỏ qua.

+0

bạn có thể giải thích những gì 'Field modifiersField = Field.class.getDeclaredField (" modifiers "); modifiersField.setAccessible (true); modifiersField.setInt (field, field.getModifiers() & ~ Modifier.FINAL); field.set (null, newValue); 'không? –

+0

Chỉnh sửa câu trả lời một chút với một số giải thích được thực hiện, cũng như liên kết đến bài viết gốc là ý tưởng để "sửa đổi" các "công cụ sửa đổi" để cho phép chúng tôi tự hack vào của nó. – Walls

+0

cảm ơn! tôi muốn mã kiểm tra vẫn tôn trọng mã nguồn, vì vậy sẽ có một 'modifiersField.setInt (trường, field.getModifiers() & Modifier.FINAL); modifiersField.setAccessible (false); 'hoạt động sau khi chúng ta thiết lập giá trị chúng ta muốn. tức là một điều "làm/hoàn tác". –

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