2012-06-24 3 views
6

Qual è l'approccio corretto per testare un riferimento debole in Java?Test WeakReference

mia idea iniziale è quella di effettuare le seguenti operazioni:

public class WeakReferenceTest { 

    public class Target{ 
     private String value;  

     public Target(String value){ 
      this.value = value; 
     }  
     public String toString(){ 
      return value; 
     } 
    } 

    public class UsesWeakReference{  
     WeakReference<Target> reference; 

     public UsesWeakReference(Target test){ 
      reference = new WeakReference<Target>(test); 
     }  
     public String call(){ 
      Target test = reference.get(); 
      if(test != null){ 
       return test.toString(); 
      } 
      return "empty"; 
     } 
    } 

    @Test 
    public void testWeakReference(){  
     Target target = new Target("42"); 

     UsesWeakReference usesWeakReference = new UsesWeakReference(target);  
     WeakReference<Target> triggerReference = new WeakReference<Target>(target);  
     assertEquals("42", usesWeakReference.call()); 

     target = null;  
     while(triggerReference.get() != null){ 
      System.gc(); 
     } 

     assertEquals("empty", usesWeakReference.call());  
    }  
} 

La prenotazione ho circa l'approccio sta usando System.gc(), mi pare di capire che essa può comportarsi in modo diverso su diverse JVM.

risposta

5

Non esiste un metodo di prova del bombardamento al 100% che utilizza i tipi di riferimento. Il comportamento degli oggetti di riferimento dipende dal momento in cui viene eseguito il GC e non esiste un modo affidabile al 100% di forzare l'esecuzione del GC.

Il meglio che puoi fare è:

  • controllare di avere le opzioni JVM giusto assetto durante l'esecuzione dei test, e
  • scrivere il test in modo che non manca nel caso in cui System.gc() è un no-op O essere disposti a disabilitare o saltare il test, o ignorare il fallimento del test.

(Si dovrebbe essere in grado di rilevare che System.gc() viene ignorato, cercando in quantità di memoria è in uso, prima e dopo la chiamata, ad esempio chiamando Runtime.totalMemory())


In realtà, non v'è un altro "soluzione". Fate in modo che il vostro test dell'unità generi un'enorme quantità di spazzatura ... abbastanza per garantire l'attivazione della raccolta dei rifiuti. (Non è una buona idea, IMO.)

+0

Apprezzo la risposta ... convalida il mio punto di vista. –

3

Nuova risposta alla vecchia domanda; Ho trovato la tua domanda perché sto affrontando esattamente lo stesso problema: voglio scrivere un test unitario per verificare che la mia classe sotto test faccia qualcosa di molto specifico se il referente di un WeakReference diventa nullo.

Ho scritto per la prima volta un semplice caso di test che avrebbe impostato il referente su null; per chiamare poi System.gc(); e abbastanza interessante: almeno nella mia eclisse, è stato "abbastanza buono" per il mio weakRefernce.get() restituire null.

Ma chissà se funzionerà per tutti gli ambienti futuri che eseguiranno questo test unitario per gli anni a venire.

Così, dopo averci pensato un po 'di più:

@Test 
public void testDeregisterOnNullReferentWithMock() { 
    @SuppressWarnings("unchecked") 
    WeakReference<Object> weakReference = EasyMock.createStrictMock(WeakReference.class); 
    EasyMock.expect(weakReference.get()).andReturn(null); 
    EasyMock.replay(weakReference); 
    assertThat(weakReference.get(), nullValue()); 
    EasyMock.verify(weakReference); 
} 

funziona bene, anche.

Significato: la risposta generica a questo problema è una fabbrica che crea WeakReference per gli oggetti per te. Quindi, quando vuoi testare il tuo codice di produzione; fornisci una fabbrica derisa; e quella fabbrica a sua volta deriderà oggetti WeakReference; e ora hai il pieno controllo del comportamento di quell'oggetto di riferimento debole.

E il "controllo completo" è molto meglio di presumendo che il GC possa fare ciò che si spera che stia facendo.

+0

Mocking è meglio qui, dato che GC non è perfettamente deterministico né controllabile. – kerner1000

+0

Molto corretto - Ho aggiunto un altro paragrafo per renderlo più chiaro.Per la cronaca: c'è qualcos'altro che potrei fare per rendere questo upvote degno dei tuoi occhi? – GhostCat

+0

Penso che sia già abbastanza chiaro. Ho dovuto creare un altro costruttore per il mio tipo che voglio testare per fornire un WeakReference beffato (è un costruttore principalmente per testare codice pulito?;)). Forse è degno di nota che deridere WeakReference sia la cosa migliore per test riproducibili ma meno "realistici", nel senso che se si vuole testare in modo esplicito un comportamento GC inaffidabile, forse non è l'approccio migliore. (grazie per l'upvote;)) – kerner1000