2

I want to test my orderService, each order has x cartItems, each cartItem is a product. I managed to do some code, and thought I got it right, but encountered a problem :

java.lang.NullPointerException: Cannot invoke "com.proj.my.repository.OrderRepository.save(Object)" because "this.orderRepository" is null
 at com.proj.my.service.impl.OrderServiceTest.testSaveOrder(OrderServiceTest.java:68)
 at java.base/java.util.ArrayList.forEach(Unknown Source)
 at java.base/java.util.ArrayList.forEach(Unknown Source)

How can it be null ?? I managed to do unit tests to my products, and to my users, but I can't do it to my orders. My order.java

package com.proj.my.model;
import java.time.LocalDate;
import java.util.List;

import org.hibernate.annotations.CreationTimestamp;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import lombok.ToString;

@ToString
@Entity
@Table(name = "myorder")
@EntityListeners(AuditingEntityListener.class)
@JsonIgnoreProperties(value = {"createdAt"}, 
    allowGetters = true)  
public class Order {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    
    @OneToOne(cascade = CascadeType.MERGE)
    @JoinColumn(name = "userId")
    private User user;


    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, targetEntity = ShoppingCart.class)
    @JoinColumn(name = "order_id")
    private List<ShoppingCart> cartItems;
    @CreationTimestamp
    @Column(updatable = false, name = "createdAt")
    private LocalDate createdAt;

    public LocalDate getCreatedAt() {
        return createdAt;
    }

    public void setCreatedAt(LocalDate createdAt) {
        this.createdAt = createdAt;
    }

    public Order() {
    }

    public Order(User user, LocalDate createdAt, List<ShoppingCart> cartItems) {
        this.user = user;
        this.cartItems = cartItems;
        this.createdAt = createdAt;
    }

    public Order(int i, LocalDate now, List<ShoppingCart> cartItems2) {
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public User getUser() {
        return user;
    }

    public void setCustomer(User user) {
        this.user = user;
    }

    public List<ShoppingCart> getCartItems() {
        return cartItems;
    }

    public void setCartItems(List<ShoppingCart> cartItems) {
        this.cartItems = cartItems;
    }
}

My orderService.java

package com.proj.my.service;

import com.proj.my.model.Order;
import com.proj.my.model.CloudProduct;
import com.proj.my.model.ShoppingCart;
import com.proj.my.repository.OrderRepository;
import com.proj.my.repository.CloudProductRepository;

import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.util.List;
import java.util.Optional;

@Service
public class OrderService {

    private OrderRepository orderRepository;
    private CloudProductRepository cloudProductRepository;

    public OrderService(OrderRepository orderRepository, CloudProductRepository cloudProductRepository) {
        this.orderRepository = orderRepository;
        this.cloudProductRepository = cloudProductRepository;
    }

    public Order getOrderDetail(int orderId) {
        Optional<Order> order = this.orderRepository.findById(orderId);
        return order.isPresent() ? order.get() : null;
    }
    
   public List<Order> getAllOrderDetail(LocalDate yesterday, LocalDate today) {
        today = LocalDate.now();
        yesterday = today.minusDays(1);
        return orderRepository.findAllByCreatedAtBetween(yesterday, today);
    }

    public float getCartAmount(List<ShoppingCart> shoppingCartList) {

        float totalCartAmount = 0f;
        float singleCartAmount = 0f;

        for (ShoppingCart cart : shoppingCartList) {

            String cloudProductName = cart.getProductName();
            Optional<CloudProduct> product = cloudProductRepository.findByProductName(cloudProductName);
            if (product.isPresent()) {
                CloudProduct cloudproduct = product.get();
                singleCartAmount = cart.getQuantity() * cloudproduct.getpriceInEuros();
                
                totalCartAmount = totalCartAmount + singleCartAmount;
                cart.setProductId(cloudproduct.getProductId());
                cart.setAmount(singleCartAmount);
                cloudProductRepository.save(cloudproduct);
            }
        }
        return totalCartAmount;
    }

    public Order saveOrder(Order order) {
        return orderRepository.save(order);
    }
}

My orderController.java

package com.proj.my.controller;

import java.time.LocalDate;
import java.util.List;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.proj.my.dto.OrderDTO;
import com.proj.my.dto.ResponseOrderDTO;
import com.proj.my.model.Order;
import com.proj.my.model.User;
import com.proj.my.service.CloudProductService;
import com.proj.my.service.OrderService;
import com.proj.my.service.UserService;

@RestController
@RequestMapping("/api")
public class OrderController {

    private OrderService orderService;
    private CloudProductService cloudProductService;
    private UserService userService;


    public OrderController(OrderService orderService, CloudProductService cloudProductService, UserService userService) {
        this.orderService = orderService;
        this.cloudProductService = cloudProductService;
        this.userService = userService;
    }

    @GetMapping(value = "/getOrder/{orderId}")
    public ResponseEntity<Order> getOrderDetails(@PathVariable int orderId) {

        Order order = orderService.getOrderDetail(orderId);
        return ResponseEntity.ok(order);
    }

    @GetMapping(value = "/getOrder")
    public List<Order> getAllOrderDetails(LocalDate yesterday, LocalDate today) {
        return orderService.getAllOrderDetail(yesterday, today);
    }

    @PostMapping("/placeOrder")
    public ResponseEntity<ResponseOrderDTO> placeOrder(@RequestBody OrderDTO orderDTO) {

        ResponseOrderDTO responseOrderDTO = new ResponseOrderDTO();

        float amount = orderService.getCartAmount(orderDTO.getCartItems());

        User user = new User(orderDTO.getuserName(), orderDTO.getuserEmail());
        
        Integer userIdFromDb = userService.isUserPresent(user);

        if (userIdFromDb != null) {
            user.setUserId(userIdFromDb);
        }else{
            user = userService.createUser(user);
        }

        LocalDate createdAt = LocalDate.now(); 

        Order order = new Order(user, createdAt, orderDTO.getCartItems());

        order = orderService.saveOrder(order);

        responseOrderDTO.setAmount(amount);

        responseOrderDTO.setDate(com.proj.my.util.DateUtil.getCurrentDateTime());
        
        responseOrderDTO.setOrderId(order.getId());


        return ResponseEntity.ok(responseOrderDTO);
    }}

My orderRepository.java

package com.proj.my.repository;

import com.proj.my.model.Order;

import java.sql.Date;
import java.time.LocalDate;
import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

@Repository
public interface OrderRepository extends JpaRepository<Order,Integer> {

    List<Order> findAllByCreatedAtBetween(LocalDate d1, LocalDate d2);
}

And finally, my orderServiceTest.java

package com.proj.my.service.impl;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;

import com.proj.my.model.CloudProduct;
import com.proj.my.model.Order;
import com.proj.my.model.ShoppingCart;
import com.proj.my.repository.CloudProductRepository;
import com.proj.my.repository.OrderRepository;
import com.proj.my.repository.ShoppingCartRepository;
import com.proj.my.service.OrderService;
import com.proj.my.service.CloudProductService;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;

public class OrderServiceTest {
    @Mock
    private CloudProductRepository cloudProductRepository;
    private CloudProductService cloudProductService;
    private OrderRepository orderRepository;    
    private OrderService orderService;
    Order order;
    CloudProduct cloudProduct;
    ShoppingCart shoppingCart;
    private ShoppingCartRepository shoppingCartRepository;
    AutoCloseable autoCloseable;

   
    @BeforeEach
    void setUp() {
        autoCloseable = MockitoAnnotations.openMocks(this);
        cloudProductService = new CloudProductServiceImpl(cloudProductRepository);
        cloudProduct = new CloudProduct(1, "Maria", (float) 232);
        orderService = new OrderService(orderRepository, cloudProductRepository);
        shoppingCart = new ShoppingCart("Maria", 2);
        List<ShoppingCart> cartItems = Arrays.asList(shoppingCart);
        order = new Order(1, LocalDate.now(), cartItems);
    }

    @AfterEach
    void tearDown() throws Exception {
        autoCloseable.close();
    }

    @Test 
    void testSaveOrder(){
        mock(CloudProduct.class);
        mock(CloudProductRepository.class);
        mock(Order.class);
        mock(OrderRepository.class);
        mock(ShoppingCart.class);
        mock(ShoppingCartRepository.class);

        when(orderRepository.save(order)).thenReturn(order);  <----------Error here
        assertThat(orderService.saveOrder(order)).isEqualTo(order);
    }

    
}

I'm getting out of ideas, since I looked it up in the internet and I can't find anything close to be similar. Maybe I'm just not paying the right attention.

Dadinho2000
  • 77
  • 1
  • 9
  • can you please add the StackTrace. And does it work when you run the application? – Simon Martinelli Jan 04 '23 at 16:58
  • Edited it already sir, and yes, everything but this test, works well. – Dadinho2000 Jan 04 '23 at 17:18
  • You don't actually set the orderRepository property in your test class, so it remains null. You should be able to check how you've done it in your other tests, but chances are the "mock(OrderRepository.class);" is meant to return a OrderRepository instance you set in that property and so use in the test. Or just add the Mock annotation to the definition (as you've done for CloudProductRepository in the class definition). You probably need to do the latter, otherwise it will be null in the @BeforeEach setup method call (and so null within the OrderService instance each test uses. – Chris Jan 04 '23 at 20:22

2 Answers2

3

In your OrderService class you are creating a private OrderRepository repository object however the object is null.

You can resolve this problem by two of the following ways.

  • You can autowire your repository object

    @Autowired

    private OrderRepository orderRepository;

  • Or You can make your repository object Final

    private final OrderRepository orderRepository;

UPDATE

Add Lombok dependency to your maven pom.xml file

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.24</version>
    <scope>provided</scope>
</dependency>

Then add @RequiredArgsConstructor and @AllArgsConstructor to you service class. It should look like this. After these changes run your application

@AllArgsConstructor
@RequiredArgsConstructor
@Service
public class OrderService {
private final OrderRepository orderRepository;
}
Syed Anas
  • 1,459
  • 3
  • 19
  • 38
1

Test setup is incorrectly using mockito.

Try:

public class OrderServiceTest {
    @Mock
    private CloudProductRepository cloudProductRepository;
    private CloudProductService cloudProductService;
    private OrderRepository orderRepository;    
    private OrderService orderService;
    Order order;
    CloudProduct cloudProduct;
    ShoppingCart shoppingCart;
    private ShoppingCartRepository shoppingCartRepository;
    AutoCloseable autoCloseable;

   
    @BeforeEach
    void setUp() {
        autoCloseable = MockitoAnnotations.openMocks(this);
        //use a new, clean orderRepository mock for each test
        orderRepository = mock(OrderRepository.class);
        cloudProductService = new CloudProductServiceImpl(cloudProductRepository);
        cloudProduct = new CloudProduct(1, "Maria", (float) 232);
        orderService = new OrderService(orderRepository, cloudProductRepository);
        shoppingCart = new ShoppingCart("Maria", 2);
        List<ShoppingCart> cartItems = Arrays.asList(shoppingCart);
        order = new Order(1, LocalDate.now(), cartItems);
    }
  ..

    @Test 
    void testSaveOrder(){
        when(orderRepository.save(order)).thenReturn(order);  
        assertThat(orderService.saveOrder(order)).isEqualTo(order);
    }
Chris
  • 20,138
  • 2
  • 29
  • 43