I keep getting NullPointerException on all my Tests. I have a CashRegister Class with a Product object lastScannedProduct that is supposed to be initially null but then it's supposed to be changed I tried to change it by using a foreach loop that scans through a LinkedHashMap<Product, SalesRecord> salesCache that saves all the Products and Sales Records but it doesn't seem to work, I would grately appreciate any help:
class CashRegister {
private final Clock clock;
private final Printer printer;
private final UI ui;
private final SalesService salesService;
// Declare a field to keep a salesCache, which is a mapping between a Product and a SalesRecord.
// When a product gets scanned multiple times, the quantity of the salesRecord is increased.
// A LinkedHashMap has the benefit that, in contrast to the HashMap, the order in which
// the items were added is preserved.
// Declare a field to keep track of the last scanned product, initially being null.
// TODO Declare and initialize fields.
Product lastScannedProduct = null;
LinkedHashMap<Product, SalesRecord> salesCache;
int quantity = 1;
/**
* Create a business object
*
* @param clock wall clock
* @param printer to use
* @param ui to use
* @param salesService to use
*/
CashRegister(Clock clock, Printer printer, UI ui, SalesService salesService) {
this.clock = clock;
this.printer = printer;
this.ui = ui;
this.salesService = salesService;
//lastScannedProduct = null;
salesCache = new LinkedHashMap<>();
}
/**
* The scan method is triggered by scanning a product by the cashier.
* Get the product from the salesService. If the product can't be found, an UnknownProductException is thrown and the
* error message from the exception is shown on the display (ui).
* If found, check if there is a salesRecord for this product already. If not, create one. If it exists, update the quantity.
* In case a perishable product was scanned, the cashier should get a calendar on his/her display.
* The product is displayed on the display.
*
* @param barcode
*/
public void scan(int barcode) {
//TODO implement scan
//Product product = salesService.lookupProduct(barcode);
if (salesCache.containsKey(barcode)) { //TODO get the existing Sales Record and change quantity
SalesRecord existing = new SalesRecord(barcode, LocalDate.now(), lastScannedProduct.getPrice());
for (SalesRecord sr :
salesCache.values()) {
if (sr.getBarcode() == barcode) {
existing = sr;
}
}
for (Product pr :
salesCache.keySet()) {
if (pr.getBarcode() == barcode) {
lastScannedProduct = pr;
}
}
existing.increaseQuantity(1);
quantity++;
salesCache.put(lastScannedProduct, existing);
} else {
for (Product pr :
salesCache.keySet()) {
if (pr.getBarcode() == barcode) {
lastScannedProduct = pr;
}
}
SalesRecord salesRecord = new SalesRecord(barcode, LocalDate.now(), lastScannedProduct.getPrice());
salesCache.put(lastScannedProduct, salesRecord);
}
if (lastScannedProduct.isPerishable() == false) {
//salesService.lookupProduct(barcode);
ui.displayProduct(lastScannedProduct);
} else if ((lastScannedProduct.isPerishable() == true)) {
ui.displayCalendar();
//correctSalesPrice();
} else {
ui.displayErrorMessage("Unknown Product!");
//throw new UnknownProductException("");
}
}
/**
* Submit the sales to the sales service, finalizing the sales transaction.
* All salesRecords in the salesCache are stored (one-by-one) in the salesService.
* All caches are reset.
*/
public void finalizeSalesTransaction() {
//TODO implement finalizeSalesTransaction()
for (SalesRecord sr :
salesCache.values()) {
salesService.sold(sr);
}
salesCache.clear();
// SalesRecord salesRecord = new SalesRecord(lastScannedProduct.getBarcode(), clock , lastScannedProduct.getPrice())
//
// salesService.sold(salesRecord);
}
/**
* Correct the sales price of the last scanned product by considering the
* given best before date, then submit the product to the service and save
* in list.
*
* This method consults the clock to see if the product is eligible for a
* price reduction because it is near or at its best before date.
*
* Precondition is that the last scanned product is the perishable product.
* You don't need to check that in your code.
*
* To find the number of days from now till the bestBeforeDate, use
* LocalDate.now(clock).until(bestBeforeDate).getDays();
*
* Depending on the number of days, update the price in the salesRecord folowing the
* pricing strategy as described in the assignment
*
* Update the salesRecord belonging to the last scanned product if necessary, so
* update the price and set the BestBeforeDate.
*
* @param bestBeforeDate
* @throws UnknownBestBeforeException in case the best before date is null.
*/
public void correctSalesPrice(LocalDate bestBeforeDate) throws UnknownBestBeforeException {
//TODO implement correctSalesPrice
int percentOff = 0;
int remainingPricePerc = 100;
// for (Product pr :
// salesCache.keySet()) {
// if (pr.getBarcode() == barcode) {
// lastScannedProduct = pr;
// }
// }
int brc = lastScannedProduct.getBarcode();
SalesRecord sc = null;
for (SalesRecord sr :
salesCache.values()) {
if (sr.getBarcode() == brc) {
sc = sr;
}
}
if (bestBeforeDate == null) {
ui.displayErrorMessage("Best before Date is null");
} else {
//Number of days left till expiring
int left = LocalDate.now(clock).until(bestBeforeDate).getDays();
if (left >= 2) {
percentOff = 0;
remainingPricePerc = 100;
} else if (left == 1) {
percentOff = 30;
remainingPricePerc = 65;
} else if ( left == 0) {
percentOff = 65;
remainingPricePerc = 35;
} else if (left < 0) {
percentOff = 100;
remainingPricePerc = 0;
}
int beforePrice = lastScannedProduct.getPrice();
int afterPrice = beforePrice/100 * remainingPricePerc;
sc.setSalesPrice(afterPrice);
sc.setBestBeforeDate(bestBeforeDate);
}
}
/**
* Print the receipt for all the sold products, to hand the receipt to the
* customer. The receipt contains lines containing: the product description,
* the (possibly reduced) sales price per piece and the quantity, separated by
* a tab.
* The order of printing is the order of scanning, however Perishable
* products are printed first. The non-perishables afterwards.
*/
public void printReceipt() {
//TODO implement printReceipt
// Collection prko =new ArrayList();
// for (Product pr :
// salesCache.keySet()) {
// lastScannedProduct = pr;
// prko.add(lastScannedProduct.getDescription());
// prko.add(lastScannedProduct.getPrice());
//
// }
// for (SalesRecord sr :
// salesCache.values()) {
// prko.add(sr.getSalesPrice());
// prko.add(sr.getQuantity());
// }
//
// for (Entry:
// prko.stream().toArray()) {
//
// }
for (Product pr :
salesCache.keySet()) {
printer.println(pr.getDescription() + "\t" + pr.getPrice() + "\t" + quantity);
}
}
}
CashRegisterTest.Class :
@ExtendWith(MockitoExtension.class)
public class CashRegisterTest {
Product lamp = new Product("led lamp", "Led Lamp", 250, 1_234, false);
Product banana = new Product("banana", "Bananas Fyffes", 150, 9_234, true);
Product cheese = new Product("cheese", "Gouda 48+", 800, 7_687, true);
Clock clock = Clock.systemDefaultZone();
Map<String, Product> products = Map.of(
"lamp", lamp,
"banana", banana,
"cheese", cheese
);
@Mock
Printer printer;
@Mock
SalesService salesService;
@Mock
UI ui;
@Captor
private ArgumentCaptor<SalesRecord> salesRecordCaptor;
@Captor
private ArgumentCaptor<Product> productCaptor;
@Captor
private ArgumentCaptor<String> stringLineCaptor;
CashRegister cashRegister;
@BeforeEach
void setup() {
cashRegister = new CashRegister(clock, printer, ui, salesService);
}
/**
* Test that after a scan, a non perishable product is looked up and
* correctly displayed.Have a look at requirements in the JavaDoc of the
* CashRegister methods. Test product is non-perishable, e.g. led lamp.
* <ul>
* <li>Train the mocked salesService and check if a lookup has been
* done.<li>Check if the mocked UI was asked to display the
* product.<li>Ensure that ui.displayCalendar is not called.<b>NOTE
*
* @throws ps.UnknownProductException
*/
@Test
public void lookupandDisplayNonPerishableProduct() throws UnknownProductException {
//TODO 1 Implement Test Method and write necessary implementation in scan() method of CashRegister
//when()
cashRegister.scan(lamp.getBarcode());
ui.displayProduct(lamp);
assertThat(lamp.isPerishable()).isFalse();
verify(ui, times(0)).displayCalendar();
//fail( "method lookupandDisplayNonPerishableProduct reached end. You know what to do." );
}
/**
* Test that both the product and calendar are displayed when a perishable
* product is scanned.
*
* @throws UnknownProductException but don't worry about it, since you test
* with an existing product now.
*/
@Test
public void lookupandDisplayPerishableProduct() throws UnknownProductException {
//TODO 2 Implement Test Method and write necessary implementation in scan() method of CashRegister
cashRegister.scan(banana.getBarcode());
ui.displayProduct(banana);
assertThat(banana.isPerishable()).isTrue();
verify(ui).displayCalendar();
//fail( "method lookupandDisplayPerishableProduct reached end. You know what to do." );
}
/**
* Scan a product, finalize the sales transaction, then verify that the
* correct salesRecord is sent to the SalesService. Use a non-perishable
* product. SalesRecord has no equals method (and do not add it), instead
* use {@code assertThat(...).usingRecursiveComparison().isEqualTo(...)}.
* Also verify that if you print a receipt after finalizing, there is no output.
*
* @throws ps.UnknownProductException
*/
@Test
public void finalizeSalesTransaction() throws UnknownProductException {
//TODO 3 Implement Test Method and write necessary implementation in finalizeSalesTransaction() method of CashRegister
cashRegister.scan(lamp.getBarcode());
cashRegister.finalizeSalesTransaction();
SalesRecord expected = new SalesRecord(lamp.getBarcode(), LocalDate.now(), lamp.getPrice());
stringLineCaptor = ArgumentCaptor.forClass(String.class);
salesRecordCaptor = ArgumentCaptor.forClass(SalesRecord.class);
assertThat(salesRecordCaptor.capture()).usingRecursiveComparison().isEqualTo(expected);
cashRegister.printReceipt();
verify(printer, times(0)).println(stringLineCaptor.capture());
//fail( "method finalizeSalesTransaction reached end. You know what to do." );
}
/**
* Verify price reductions. For a perishable product with: 10 days till
* best-before, no reduction; 2 days till best-before, no reduction; 1 day
* till best-before, 35% price reduction; 0 days till best-before (so sales
* date is best-before date), 65% price reduction; -1 days till best-before
* (product over date), 100% price reduction.
*
* Check the correct price using the salesService and an argument captor.
*/
@ParameterizedTest
@CsvSource({
"banana,10,100",
"banana,2,100",
"banana,1,65",
"banana,0,35",
"banana,-1,0",})
public void priceReductionNearBestBefore(String productName, int daysBest, int pricePercent) throws UnknownBestBeforeException, UnknownProductException {
//TODO 4 Implement Test Method and write necessary implementation in correctSalesPrice() method of CashRegister
LocalDate days = LocalDate.now().plusDays(daysBest);
productCaptor = ArgumentCaptor.forClass(Product.class);
cashRegister.correctSalesPrice(days);
salesRecordCaptor = ArgumentCaptor.forClass(SalesRecord.class);
//salesService;
verify(salesRecordCaptor.capture()).setSalesPrice(productCaptor.capture().getPrice()/100*pricePercent);
//assertThat(cashRegister.correctSalesPrice(daysBest))
//fail( "method priceReductionNearBestBefore reached end. You know what to do." );
}
/**
* When multiple products are scanned, the resulting lines on the receipt
* should be perishable first, not perishables last. Scan a banana, led lamp
* and a cheese. The products should appear on the printed receipt in
* banana, cheese, lamp order. The printed product line on the receipt
* should contain description, (reduced) salesprice per piece and the
* quantity.
*
*/
@Test
public void printInProperOrder() throws UnknownBestBeforeException, UnknownProductException {
//TODO 5 Implement Test Method and write necessary implementation in printReceipt() method of CashRegister
cashRegister.scan(lamp.getBarcode());
cashRegister.scan(banana.getBarcode());
cashRegister.scan(cheese.getBarcode());
StringBuilder str = new StringBuilder();
str.append(lamp.toString());
str.append(banana.toString());
str.append(cheese.toString());
stringLineCaptor = ArgumentCaptor.forClass(String.class);
List<Product> expected = new ArrayList<>();
salesRecordCaptor = ArgumentCaptor.forClass(SalesRecord.class);
List<SalesRecord> actual = salesRecordCaptor.getAllValues();
cashRegister.printReceipt();
InOrder inOrder = Mockito.inOrder();
verify(stringLineCaptor.capture()).equals(str.toString());
// fail( "method printInProperOrder reached end. You know what to do." );
}
/**
* Test that invoking correctSalesPrice with null parameter results in
* exception.
*
* @throws UnknownProductException (but that one is irrelevant). First scan
* (scan) a perishable product. Afterwards invoke correctSalesPrice with
* null parameter. An UnknownProductException should be thrown.
*/
@Test
public void correctSalesPriceWithBestBeforeIsNullThrowsException() throws UnknownProductException {
try {
Product rhino = new Product("rhino", "Rhino", 250, 1_234, true);
cashRegister.scan(rhino.getBarcode());
LocalDate da = null;
cashRegister.correctSalesPrice(da);
fail("Should have thrown UnknownBestBeforeException");
}
catch (UnknownBestBeforeException e){
System.out.println(e);
}
//TODO 6 Implement Test Method and write necessary implementation in correctSalesPrice() method of CashRegister
//fail( "method correctSalesPriceWithBestBeforeIsNull reached end. You know what to do." );
}
/**
* Test scanning an unknown product results in error message on GUI.
*/
@Test
public void lookupUnknownProductShouldDisplayErrorMessage() throws UnknownProductException {
//TODO 7 Implement Test Method and write necessary implementation in scan() method of CashRegister
Product rhino = new Product("rhino", "Rhino", 250, 1_234, false);
stringLineCaptor = ArgumentCaptor.forClass(String.class);
cashRegister.scan(rhino.getBarcode());
//when(cashRegister.scan(rhino.getBarcode())).thenThrow(UnknownProductException.class);
verify(ui).displayErrorMessage(stringLineCaptor.capture());
//fail( "method lookupUnknownProduct... reached end. You know what to do." );
}
/**
* Test that a product that is scanned twice, is registered in the
* salesService with the proper quantity AND make sure printer prints the
* proper quantity as well.
*
* @throws UnknownProductException
*/
@Test
public void scanProductTwiceShouldIncreaseQuantity() throws UnknownProductException {
cashRegister.scan(lamp.getBarcode());
cashRegister.scan(lamp.getBarcode());
verify(salesService, times(2)).lookupProduct(lamp.getBarcode());
//TODO 8 Implement Test Method and write necessary implementation in scan() method of CashRegister
//fail( "method scanProductTwice reached end. You know what to do." );
}
}
ErrorMessage:
java.lang.NullPointerException
at ps.CashRegister.scan(CashRegister.java:92)
at ps.CashRegisterTest.lookupandDisplayNonPerishableProduct(CashRegisterTest.java:81)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:567)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:688)
at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:210)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:206)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:131)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:65)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1507)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1507)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:96)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:75)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)