Search code examples
mysqlspring-bootunit-testingjpacrud-repository

java.lang.NullPointerException: Cannot invoke "com.proj.my.repository.OrderRepository.save(Object)" because "this.orderRepository" is null


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.


Solution

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