Why do I get NPE, which appears only when the program is running?

I am writing a test class for my GiftSelector class using JUnit in BlueJ. When I run the testGetCountForAllPresents() method, I get a NullPointerException in the line:

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

The strange thing about this NPE is that it rarely appears when I run a test once, but often appears a second time when I run a test. Sometimes this does not appear until I checked the test 7-8 times in a row.

The error message I get is an exception message.

NPE on line 215 in GiftSelectortest.testGetCountForAllPresents

Code for my test class:

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

I have included only those testing methods that reference the getCountsForAllPresents() method. You will notice that I added print statements before each call to the assertEquals() method, containing the getCountForAllPresents() method. Interestingly, in front of the line where I get the NPE, the print statement prints the correct value for the HashMap returned by getCountForAllPresents() .

The only other strange thing I noticed is that when I look at the testGetCountForAllPresents() method using BlueJ's built-in debugger, I notice that giftList3 does not display in santaMap HashMap in santasSelector , but the print statement still prints the correct count , implying that he should know about giftList3 .

Code for 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 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 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 map, return null return null; } } 

I have to explain that santaMap is a HashMap , with a Child object as a key and a GiftList object as a value. It basically displays the baby on his Christmas wish list. A santaMap can contain only one wish list with the same child.

I don’t know why I get NPE, is it somehow related to the way I wrote the getCountForAllPresents() method? How did I apply the test method / class?

+6
source share
1 answer

Your Present class does not override hashCode() and equals() . This means that banana1 and banana are two different keys in any HashMap that will use them as the key.

So let's see what happens here. You have objects banana and banana1 - two of the first, one of the second.

Inside getCountsForAllPresents() you have two hash cards. The first goes along the line of comparison of objects, and the second - along the objects themselves.

You add the first banana you come across. If it is a banana object, you will have something like this:

  uniquePresents
 banana-fruit-10 ➞ [banana instance]

 presentFrequency
 [banana instance] ➞ Integer (1)

You keep repeating. You will see the following banana object. This is the same object. You'll get:

  uniquePresents
 banana-fruit-10 ➞ [banana instance]

 presentFrequency
 [banana instance] ➞ Integer (2)

Now you are in the banana1 object. This is a different object, but it has the same comparison string! What's happening?

This condition is true: uniquePresents.containsKey(present.toComparisonString()) . This means that it goes into the true if part.

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

This means that the object currently pointed to by the banana-fruit-10 object, which is the banana object and not the banana1 object, receives the frequency associated with it and increases it. It is also stored at one facility. Now you have:

  uniquePresents
 banana-fruit-10 ➞ [banana instance]

 presentFrequency
 [banana instance] ➞ Integer (3)

Note that presentFrequency does not have a banana1 key. And now you are returning this object.

When you try to restore banana , it works fine - assert works.

But remember, santaMap itself is a HashMap . This means that it does not have a guaranteed order. An iterator can give you giftList1 , giftList2 , giftList3 , but it can also give you giftList3 , giftList1 , giftList2 - or any other order.

So what happens when he gives you giftList3 in the first place? You will end up:

  uniquePresents
 banana-fruit-10 ➞ [ banana1 instance]

 presentFrequency
 [ banana1 instance] ➞ Integer (3)

Why? Because banana1 was the first gift with the banana-fruit-10 key and what it will use from now on.

When this happens, when you try to get banana from the returned object, this key does not exist in the frequency list. It returns null - and there is your NullPointerException .

+4
source

Source: https://habr.com/ru/post/980607/


All Articles