2012-03-03 13 views
9

Mam przypadek testowy JUnit w Android projektu, który zawiera kod, który wygląda tak:„private static final” członek Android testów jednostkowych wartość zmienia klasy 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); 
} 

Ta klasa Test ma wiele metod testowych , z których niektóre odnoszą się do (ale nie próbuj modyfikować) wartości tej stałej. Jednak, kiedy uruchomić te testy (Android 2.2.2), wszystkie z tych badań, ale pierwszy z nich się nie powiedzie, a logcat mi pokazuje to:

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 

Jak statyczną wartość końcowa zmiana pola w taki sposób? Jak temu zapobiec? Czy są inne sytuacje, w których może się to zdarzyć?

--- EDIT 1

I teraz przycięte kodu w dół w mniejszym przykładzie, że mogą być włączone w całości. Zobacz poniżej:

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

Po uruchomieniu testu testA przechodzi, ale testB nie działa. Jeśli testA zostanie skomentowany, testB przechodzi.

Wydaje się ważne, że jest to ServiceTestCase. Standardowa JUnit TestCase nie powoduje problemu. Jeśli "CONST2" jest ciągiem, oba testy przebiegają zgodnie z oczekiwaniami. Każdy inny typ odniesienia wydaje się odtworzyć problem.

+0

Czy istnieje możliwość wyładowania i ponownego załadowania klasy testowej? Czy Dalvik może to zrobić? Może twoja stała jest w cieniu przez coś innego? – nmr

+0

W projekcie nie ma nic więcej o tej samej nazwie, więc nie podejrzewam, że jest to problem z cieniami. Myślę, że JUnit próbuje przeładować klasy między testami, używając innej klasy ClassLoader dla każdego wywołania. Zatem pole * może * być inną instancją od drugiego uruchomienia.W takim przypadku problem jest mniejszy, niż się zmienił, i więcej, że inicjator nie uruchomił się po raz drugi. – Jules

+1

Nie, JUnit nie ładuje ponownie klas, tylko ponownie tworzy instancję klasy przed każdą metodą. –

Odpowiedz

7

Wydaje się, że AndroidTestCase wykorzystuje odbicie ustawić wszystkie non-prymitywne pola do null po każdej próbie w scrubClass. Nie sprawdza, czy pola są statyczne czy ostateczne, więc wydaje się, że jest to źródło problemu.

Aby rozwiązać problem, zmień pole na nie ostateczne i ustaw je wewnątrz setUp. Upewnij się również, że wywołujesz super.setUp() jako pierwszą linię twojego setUp, aby upewnić się, że walizka testowa została poprawnie zainicjalizowana.

+0

Dalvik pozwala na to? Czy inne maszyny JVM na to zezwalają? To również nie jive z "Wydaje się ważne, że jest to ServiceTestCase. Standardowa JUnit TestCase nie powoduje problemu. Jeśli "CONST2" jest ciągiem, oba testy przebiegają zgodnie z oczekiwaniami. Każdy inny typ odniesienia wydaje się odtwarzać problem. " – nmr

+2

Po prostu próbowałem go w zwykłej maszynie JVM, a 'setAccessible' nie ma żadnego efektu; tj. nie mogę zastąpić ostatecznej wartości. Przeglądając 'scrubClass' ponownie nie jestem dokładnie pewien, co robi. Dokumentacja nie pasuje do implementacji, ale nie wiem dokładnie, jak się ją nazywa. Wygląda na to, że zeruje tylko pola, które same w sobie są przypadkami testowymi, co wydaje się dość dziwne. Użyłbym obejścia i odejdę, chyba że jesteś naprawdę ciekawy, aby dalej kopać. :) –

+6

Okazało się, że jest to znany błąd w systemie Android od prawie 3 lat. http://code.google.com/p/android/issues/detail?id=4244 – Jules

0

Jeśli to nie jest problem z określeniem zakresu, czy możliwe jest usunięcie wartości odniesienia.

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

więc, jeśli TasksService zmiany lub wywołanie resolve() nie powiedzie się, że będzie to zrobić.

+0

TasksService.TASKLIST_RESOURCELIST_URL jest stałą, więc nie powinno być możliwe, że się zmienia. A resolve() generuje wyjątek IllegalArgumentException, jeśli się nie powiedzie, co powinno doprowadzić do wyjątku ExceptionInInitializerError, więc to nie brzmi jak problem. – Jules

1

Czy to możliwe, że śmieciarz pozbywa się go, ponieważ nie może wiedzieć, że nadal uzyskuje dostęp do obiektu przy użyciu JUnit?

Może powinieneś stworzyć metodę, która początkowo ustawia wartość. Nie zapomnij zanotować tego przy pomocy @Begin, aby zapewnić poprawną inicjalizację tej zmiennej.

W każdym razie w teście nie powinieneś zakładać, że poprzednie operacje powiodły się. Środowisko, którego potrzebujesz w zadaniu JUnit, powinno być budowane od zera za każdym razem.

pozdrowienia

+0

Jeśli tak, to wygląda na to, że jest to buga. GC nie powinien móc zbierać obiektów, do których można dotrzeć (z wyjątkiem za pośrednictwem WeakRef <> i podobnych). Ponadto, gdyby tak się działo, spodziewałbym się awarii maszyny wirtualnej, ponieważ nie wiedziałaby ona, że ​​zwisające odwołanie nie jest nadal poprawne, i spróbowałaby go użyć zamiast zastąpić go wartością null. – Jules

+0

@Begin nie ma zastosowania; to wydaje się być dodatkiem JUnit 4, a Android używa JUnit 3. I nie zakładam, że poprzednie operacje powiodły się - po prostu używam stałej, która powinna być obliczona tylko raz w czasie inicjalizacji klasy. – Jules

Powiązane problemy