2012-03-03 15 views
9

Tôi có một trường hợp thử nghiệm JUnit trong một dự án Android có chứa mã trông như thế này:'private static thức' thành viên của Android đơn vị kiểm tra giá trị thay đổi lớp null

private static final URI TEST_RESOURCE_URL = TasksService.TASKLIST_RESOURCELIST_URL.resolve("task/test.task"); 

public void setUp() { 
    Log.i("Test", "TEST_RESOURCE_URL=" + TEST_RESOURCE_URL); 
} 

lớp thử nghiệm này có nhiều phương pháp thử , một số tham chiếu đến (nhưng không cố gắng sửa đổi) giá trị của hằng số này. Tuy nhiên, khi tôi chạy những thử nghiệm này (Android 2.2.2), tất cả các xét nghiệm nhưng một trong những đầu tiên thất bại, và logcat cho tôi thấy điều này:

03-03 18:56:41.791: I/Test(12008): TEST_RESOURCE_URL=http://apate.meridiandigital.net/tasks/task/test.task 
03-03 18:56:42.101: I/Test(12008): TEST_RESOURCE_URL=null 
03-03 18:56:42.131: I/Test(12008): TEST_RESOURCE_URL=null 
03-03 18:56:42.151: I/Test(12008): TEST_RESOURCE_URL=null 
03-03 18:56:42.281: I/Test(12008): TEST_RESOURCE_URL=null 
03-03 18:56:42.311: I/Test(12008): TEST_RESOURCE_URL=null 
03-03 18:56:42.341: I/Test(12008): TEST_RESOURCE_URL=null 
03-03 18:56:42.361: I/Test(12008): TEST_RESOURCE_URL=null 
03-03 18:56:42.391: I/Test(12008): TEST_RESOURCE_URL=null 
03-03 18:56:42.391: I/Test(12008): TEST_RESOURCE_URL=null 

như thế nào một trận chung kết có giá trị thay đổi lĩnh vực tĩnh như thế này? Làm cách nào để ngăn điều này xảy ra? Có những tình huống khác mà nó có thể xảy ra không?

--- EDIT 1

bây giờ tôi đã tỉa mã xuống một ví dụ nhỏ có thể được đưa vào toàn bộ. Xem bên dưới:

public class MyService extends Service { 
    @Override 
    public IBinder onBind(Intent intent) { 
     return null; 
    } 
} 


public class StaticFinalTest extends ServiceTestCase<MyService> { 
    public StaticFinalTest() { 
     super(MyService.class); 
    } 

    public static final Object CONST2 = new Object(); 

    public void testA() 
    { 
     assertNotNull (CONST2); 
    } 

    public void testB() 
    { 
     assertNotNull (CONST2); 
    } 
} 

Khi thử nghiệm này chạy, testA vượt qua nhưng testB không thành công. Nếu testA được nhận xét, testB sẽ vượt qua.

Điều quan trọng là nó là ServiceTestCase. Một JUnit TestCase tiêu chuẩn không gây ra vấn đề. Nếu 'CONST2' là một Chuỗi, cả hai bài kiểm tra đều như mong đợi. Bất kỳ loại tài liệu tham khảo nào khác dường như tái tạo vấn đề.

+0

Lớp học thử nghiệm có thể không được tải và tải lại không? Thậm chí Dalvik có thể làm điều đó không? Có lẽ hằng số của bạn đang bị che khuất bởi cái gì khác? – nmr

+0

Không có gì khác có cùng tên trong bất kỳ dự án nào, vì vậy tôi không nghi ngờ đó là vấn đề về bóng tối. Tôi nghĩ rằng JUnit không cố gắng để tải lại các lớp học giữa chạy thử nghiệm, mặc dù, bằng cách sử dụng một ClassLoader khác nhau cho mỗi lời gọi. Vì vậy, trường * có thể * là một thể hiện khác từ lần chạy thứ hai trở đi.Trong trường hợp đó, vấn đề là ít hơn nó đã thay đổi, và nhiều hơn nữa mà initialiser đã không chạy lần thứ hai xung quanh. – Jules

+1

Không, JUnit không tải lại các lớp, nó chỉ tái khởi tạo lớp trước mỗi phương thức. –

Trả lời

7

Dường như AndroidTestCase sử dụng phản ánh để đặt tất cả các lĩnh vực phi nguyên thủy để null sau mỗi lần thử nghiệm trong scrubClass. Nó không kiểm tra xem các trường là tĩnh hay cuối cùng, do đó, điều này có vẻ là nguồn gốc của vấn đề.

Để giải quyết, hãy thay đổi trường thành trường không phải là cuối cùng và đặt trường trong setUp. Ngoài ra, hãy đảm bảo bạn gọi số super.setUp() làm dòng đầu tiên của số setUp để đảm bảo rằng trường hợp thử nghiệm được khởi tạo đúng cách.

+0

Dalvik cho phép điều này xảy ra? Các JVM khác có cho phép điều đó không? Điều này cũng không jive với 'Nó có vẻ quan trọng là nó là một ServiceTestCase. Một JUnit TestCase tiêu chuẩn không gây ra vấn đề. Nếu 'CONST2' là một Chuỗi, cả hai bài kiểm tra đều như mong đợi. Bất kỳ loại tài liệu tham khảo nào khác dường như tái tạo vấn đề. ' – nmr

+2

Tôi vừa thử nó trong một JVM thông thường, và 'setAccessible' không có hiệu lực; tức là tôi không thể ghi đè giá trị cuối cùng. Nhìn lại 'scrubClass' một lần nữa tôi không chắc chắn nó là gì. Các tài liệu dường như không phù hợp với việc thực hiện, nhưng tôi không biết chính xác nó được gọi như thế nào. Có vẻ như nó chỉ vô hiệu hóa các trường tự kiểm tra các trường hợp mà có vẻ khá kỳ quặc. Tôi sẽ sử dụng cách giải quyết và để cho nó đi trừ khi bạn thực sự tò mò để đào sâu hơn. :) –

+6

Hóa ra đây là lỗi đã biết trong Android trong gần 3 năm. http://code.google.com/p/android/issues/detail?id=4244 – Jules

0

Nếu nó không phải là vấn đề phạm vi, có thể bạn đang bỏ giá trị tham chiếu.

private static final URI TEST_RESOURCE_URL = TasksService.TASKLIST_RESOURCELIST_URL.resolve("task/test.task"); 

như vậy, nếu TasksService thay đổi, hoặc cuộc gọi đến resolve() thất bại, điều đó sẽ làm điều đó.

+0

TasksService.TASKLIST_RESOURCELIST_URL chính nó là một hằng số, do đó không thể thay đổi được. Và giải quyết() ném IllegalArgumentException nếu nó không thành công, mà sẽ dẫn đến một ExceptionInInitializerError, mà không xảy ra, do đó, không âm thanh như vấn đề, một trong hai. – Jules

1

Có thể bộ thu gom rác sẽ loại bỏ nó vì nó không thể biết bạn vẫn đang truy cập đối tượng bằng JUnit?

Có lẽ bạn nên tạo phương thức đặt giá trị ban đầu. Đừng quên chú thích nó với @Begin để đảm bảo việc khởi tạo chính xác biến này.

Dù sao trong trường hợp thử nghiệm, bạn không nên giả định rằng các hoạt động trước đó đã thành công. Môi trường bạn cần trong một nhiệm vụ JUnit nên được xây dựng từ số không mỗi lần.

liên quan

+0

Nếu có, nó có vẻ như là một lỗi thu gom rác. Nó không nên có khả năng cho GC để thu thập một đối tượng có thể đạt được (ngoại trừ thông qua WeakRef <> và tương tự). Ngoài ra, nếu điều này xảy ra, tôi hy vọng VM sẽ sụp đổ, vì nó sẽ không biết rằng tham chiếu lơ lửng vẫn không hợp lệ, và sẽ cố gắng theo nó thay vì thay thế bằng null. – Jules

+0

@Begin không áp dụng; mà dường như là một bổ sung JUnit 4, và Android sử dụng JUnit 3. Và tôi không giả sử các hoạt động trước đó đã thành công - tôi chỉ đơn giản là sử dụng một hằng số cần được tính một lần duy nhất tại thời gian khởi tạo lớp. – Jules

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