2015-01-06 20 views
6

Sto scrivendo una classe di test per la mia classe GiftSelector utilizzando JUnit in BlueJ. Quando eseguo il metodo testGetCountForAllPresents(), ho un NullPointerException sulla linea:Perché sto ricevendo un NPE che appare solo occasionalmente quando il programma è in esecuzione?

assertEquals(true, santasSelector.getCountsForAllPresents().get(banana) == 3); 

La cosa strana di questo NPE, è che sembra raramente quando si esegue il test di una volta, ma appare spesso la seconda volta che corro il test. A volte non appare finché non eseguo il test 7-8 volte consecutivamente.

Il messaggio di errore che sto ottenendo è: nessun messaggio di eccezione.

NPE alla linea 215 in GiftSelectortest.testGetCountForAllPresents

Il codice per la mia classe di test è:

import static org.junit.Assert.*; 
import org.junit.After; 
import org.junit.Before; 
import org.junit.Test; 

/** 
* The test class GiftSelectorTest. The GiftSelector that you are 
* testing must have testMode enabled for this class to function. 
* This is done in the setUp() method. 
*/ 
public class GiftSelectorTest 
{ 
    private GiftList giftList1; 
    private GiftList giftList2; 
    private GiftList giftList3; 
    private Child jack; 
    private Child bob; 
    private Child dave; 
    private Child naughty1; 
    private GiftSelector santasSelector; 
    private Present banana1; 
    private Present orange; 
    private Present banana; 
    private Present apple; 
    private Present bike; 
    private Present doll; 
    private Present got; 
    private Present pearlHarbour; 
    private Present dog; 
    private Present cat; 
    private Present ball; 
    private Present heineken; 

    /** 
    * Default constructor for test class GiftSelectorTest 
    */ 
    public GiftSelectorTest() 
    { 
     //Nothing to do here... 
    } 

    /** 
    * Sets up the test fixture. 
    * 
    * Called before every test case method. 
    */ 
    @Before 
    public void setUp() 
    { 
     santasSelector = new GiftSelector(); 
     santasSelector.setTestMode(true); 
     jack = new Child("Jack", 20, "1 A Place", true, true, true, false); 
     bob = new Child("Bob", 10, "2 A Place", true, true, true, true); 
     dave = new Child("Dave", 10, "3 A Place", true, true, true, true); 
     naughty1 = new Child("John", 5, "4 A Place", true, true, true, true); 
     giftList1 = new GiftList(jack); 
     giftList2 = new GiftList(bob); 
     giftList3 = new GiftList(dave); 
     banana = new Present("banana", "fruit", 10); 
     orange = new Present("orange", "fruit", 10); 
     banana1 = new Present("banana", "fruit", 10); 
     apple = new Present("apple", "fruit", 10); 
     bike = new Present("bike", "toy", 200); 
     doll = new Present("doll", "toy", 40); 
     got = new Present("game of thrones", "dvd", 50); 
     pearlHarbour = new Present("pearl harbour", "dvd", 20); 
     dog = new Present("dog", "animal", 100); 
     cat = new Present("cat", "animal", 80); 
     ball = new Present("ball", "toy", 5); 
     heineken = new Present("heineken", "beer", 1.60); 
    } 

    /** 
    * Tears down the test fixture. 
    * 
    * Called after every test case method. 
    */ 
    @After 
    public void tearDown() 
    { 
     //Nothing to do here... 
    } 


    @Test 
    public void testGetCountForAllPresents() 
    { 
     System.out.println(santasSelector.getCountsForAllPresents()); 
     //Test on empty GiftSelector 
     assertNull(santasSelector.getCountsForAllPresents()); 

     //Test on a GiftSelector with one giftlist containing one present 
     giftList1.addPresent(banana); 
     santasSelector.addGiftList(giftList1); 
     System.out.println(santasSelector.getCountsForAllPresents()); 
     assertEquals(true, santasSelector.getCountsForAllPresents().get(banana) == 1); 

     //Test when GiftSelector contains 2 giftlists, each containing the same present object 

     giftList2.addPresent(banana); 
     santasSelector.addGiftList(giftList2); 
     System.out.println(santasSelector.getCountsForAllPresents()); 
     assertEquals(true, santasSelector.getCountsForAllPresents().get(banana) == 2); 

     //Test when GiftSelector contains 3 giftlists, 2 containing the same present object and another containing an identical present but with a different present instance 
     giftList3.addPresent(banana1); 
     santasSelector.addGiftList(giftList3); 
     System.out.println(santasSelector.getCountsForAllPresents()); 
     assertEquals(true, santasSelector.getCountsForAllPresents().get(banana) == 3); //This is the line I get the NPE 

     //Test when GiftSelector contains 3 giftLists, the first with one with a banana, the second with a banana and apple, and the third with a banana1 and ball 
     giftList2.addPresent(apple); 
     giftList3.addPresent(ball); 
     System.out.println(santasSelector.getCountsForAllPresents()); 
     assertEquals(true, santasSelector.getCountsForAllPresents().get(banana) == 3); 
     assertEquals(true, santasSelector.getCountsForAllPresents().get(apple) == 1); 
     assertEquals(true, santasSelector.getCountsForAllPresents().get(ball) == 1); 

    } 


    @Test 
    public void testGetMostPopularPresent() 
    { 
     //Test on empty GiftSelector 
     assertNull(santasSelector.getMostPopularPresent()); 

     //Test on a GiftSelector with one giftList and one Present 
     giftList1.addPresent(heineken); 
     santasSelector.addGiftList(giftList1); 
     assertEquals(true, santasSelector.getMostPopularPresent().comparePresent(heineken)); 

     //Tset on a GiftSelector with 1 giftList and 2 presents, one more expensive than the other 
     giftList1.addPresent(banana); 
     assertEquals(true, santasSelector.getMostPopularPresent().comparePresent(banana)); 

     //Test on a GiftSelector with 1 giftList and 3 presents. Banana and Apple are equal in price, and are both in the top3, 
     //therefore it should return the present closest to the start of the list 
     giftList1.addPresent(apple); 
     assertEquals(true, santasSelector.getMostPopularPresent().comparePresent(banana) || santasSelector.getMostPopularPresent().comparePresent(apple)); 

     //Test on a GiftSelector with 2 giftLists, the second list containing banana1, an indentical present to banana 
     giftList2.addPresent(banana1); 
     santasSelector.addGiftList(giftList2); 
     assertEquals(true, santasSelector.getMostPopularPresent().comparePresent(banana)); 

     //Test on a GiftSelector with 2 giftLists, the first containing four presents and the second containing 2 presents. 
     //This tests to see if top3 is working. 
     giftList1.addPresent(bike); 
     giftList2.addPresent(bike); 
     assertEquals(true, santasSelector.getMostPopularPresent().comparePresent(bike)); 
    } 
} 

ho incluso solo i metodi di prova che fanno riferimento al metodo di getCountsForAllPresents(). Si noterà che ho aggiunto dichiarazioni di stampa prima di ogni chiamata a un metodo assertEquals() contenente il metodo getCountForAllPresents(). La cosa interessante è che prima della riga in cui ottengo l'NPE, l'istruzione print stampa il valore corretto perrestituito da getCountForAllPresents().

L'unica altra cosa strana che ho notato, è che quando vado attraverso il metodo testGetCountForAllPresents() utilizzando incorporato nel debugger, mi accorgo che giftList3 non appare nel santaMapHashMap in santasSelector, eppure l'istruzione print ancora BlueJ stampa il conteggio corretto, implicando che deve conoscere giftList3.

Il codice per getCountForAllPresents() è:

/** 
* For each present, calculate the total number of children who have asked for that present. 
* 
* @return - a Map where Present objects are the keys and Integers (number of children requesting 
* a particular present) are the values. Returns null if santaMap is empty. 
*/ 
public HashMap<Present, Integer> getCountsForAllPresents() 
{ 
    if(!santaMap.isEmpty()) { 
     //This HashMap contains a mapping from each unique real world present, represented by it's toComparisonString(), to a Present object representing it 
     HashMap<String, Present> uniquePresents = new HashMap<String, Present>(); 
     //This HashMap contains a mapping from each Present object in uniquePresents to the number of times it's toComparisonString() is equal to another in santaMap 
     HashMap<Present, Integer> presentFrequency = new HashMap<Present, Integer>(); 

     for(GiftList wishlist: santaMap.values()) { 
      for(Present present: wishlist.getAllPresents()) { 
       //Have we already seen this present? 
       if(uniquePresents.containsKey(present.toComparisonString())) { 
        //If so, update the count in presentFrequency 
        Integer tmp = presentFrequency.get(uniquePresents.get(present.toComparisonString())); 
        tmp++; 
        presentFrequency.put(uniquePresents.get(present.toComparisonString()), tmp); 
       } else { 
        //If not, add it to the maps uniquePresents and presentFrequency (with a frequency of 1) 
        uniquePresents.put(present.toComparisonString(), present); 
        presentFrequency.put(present, 1); 
       } 
      } 
     } 
     //Return a map with unique presents as keys and their frequencies as values 
     return presentFrequency; 
    } 
    else { 
     //If there are no mappings in Santa's map, return null 
     return null; 
    } 
} 

dovrei spiegare che santaMap è un HashMap, con un oggetto Child come chiave e un oggetto GiftList come valore. Fondamentalmente mappa un bambino nella lista dei desideri di Natale. Un santaMap può contenere solo una lista dei desideri dello stesso bambino.

Non ho idea del motivo per cui sto ricevendo l'NPE, è qualcosa a che fare con il modo in cui ho scritto il metodo getCountForAllPresents()? Come ho implementato il metodo/la classe di test?

+0

Puoi aggiungere il codice per 'Present'? – RealSkeptic

+1

dove si trova la riga 215 in cui si ottiene il valore null? –

+0

Penso che il codice completo di 'GiftSelector' potrebbe essere una buona aggiunta –

risposta

4

La classe Present non sovrascrive hashCode() e equals(). Ciò significa che banana1 e banana sono due chiavi distinte in qualsiasi HashMap che li utilizzerà come chiave.

Quindi vediamo cosa succede qui. Hai gli banana e gli oggetti banana1 - due dei primi, uno dei secondi.

All'interno di getCountsForAllPresents() si dispone delle due mappe di hash. Il primo passa per la stringa di confronto degli oggetti e il secondo per gli oggetti stessi.

Aggiungete la prima banana che incontrate.Se è l'oggetto banana, avrete qualcosa di simile:

uniquePresents 
banana-fruit-10 ➞ [banana instance] 

presentFrequency 
[banana instance] ➞ Integer(1) 

si continua a scorrere. Si incontra il prossimo oggetto banana. È lo stesso oggetto. Otterrete:

uniquePresents 
banana-fruit-10 ➞ [banana instance] 

presentFrequency 
[banana instance] ➞ Integer(2) 

Ora si arriva a l'oggetto banana1. È un oggetto diverso, ma ha la stessa stringa di confronto! Che succede?

Questa condizione è vera: uniquePresents.containsKey(present.toComparisonString()). Ciò significa che entra nella parte vera di if.

Integer tmp = presentFrequency.get(uniquePresents.get(present.toComparisonString())); 

Ciò significa che ci vorranno all'oggetto attualmente puntato da banana-fruit-10, che è l'oggetto banana - non l'oggetto banana1, ottenere la sua frequenza associata, e incrementarlo. Memorizza anche dallo stesso oggetto. Quello che abbiamo ora è:

uniquePresents 
banana-fruit-10 ➞ [banana instance] 

presentFrequency 
[banana instance] ➞ Integer(3) 

noti che presentFrequency non dispone di una chiave banana1 a tutti. E ora restituisci questo oggetto.

Quando si tenta di recuperare da banana, funziona OK - l'assert funziona.

Ma ricorda, santaMap stesso è un HashMap. Ciò significa che non esiste un ordine garantito su di esso. L'iteratore può darti giftList1, giftList2, giftList3, ma potrebbe anche darti giftList3, giftList1, giftList2 - o qualsiasi altro ordine.

Quindi cosa succede quando ti dà giftList3 prima? Finirai con:

uniquePresents 
banana-fruit-10 ➞ [banana1 instance] 

presentFrequency 
[banana1 instance] ➞ Integer(3) 

Perché? Perché è stato il primo regalo con la chiave banana-fruit-10, ed è quello che userà d'ora in poi.

Quando ciò accade, quando si tenta di ottenere banana dall'oggetto restituito, tale chiave non esiste nell'elenco di frequenze. Restituisce null - e c'è il tuo NullPointerException.

+0

Grazie per aver spiegato questo in modo così chiaro. Vedi qualche modo per risolvere questo problema in modo che restituisca sempre la risposta corretta? Sono abbastanza nuovo per la programmazione, quindi non ho ancora coperto l'override del metodo e altre funzionalità di OO, C'è una soluzione migliore rispetto a sostituire hashCode() ed Equals() in Present? – JC2188

+0

Bene, non se si intende utilizzare questi oggetti come chiavi in ​​una mappa hash. Ma forse puoi usare qualcos'altro, come un oggetto che contiene una mappa di hash (codificata dalla stringa di confronto), e ha un metodo 'get' che prende un oggetto' Present' come parametro e restituisce la frequenza dalla mappa hash interna dalla sua stringa di confronto. Quindi usa quell'oggetto come tipo di ritorno di 'getCountsForAllPresents()'. – RealSkeptic

Problemi correlati