Testing service that has attribut Repository that implements CrudRepository<> in - java

I want to test this service that uses 3 repositorie implementing CrudRepository of JPA
#Service
public class ExpeditionService {
ArticleRepository articleRepository;
CustomerRepository customerRepository;
AdressRepository adressRepository;
public ExpeditionService(ArticleRepository articleRepository, CustomerRepository customerRepository, AdressRepository adressRepository) {
this.articleRepository = articleRepository;
this.customerRepository = customerRepository;
this.adressRepository = adressRepository;
}
public Set<Article> findArticleByOrderId(Long orderId) {
return articleRepository.findArticleByOrdersId(orderId);
}
public Optional<Customer> findCustomerById(Long customerId) {
return customerRepository.findById(customerId);
}
public Set<Adress> findAdressesByOrderId(Long orderId) {
return adressRepository.findAdressesByOrOrderAdressesId(orderId);
}
}
In my test I set up ExpeditionService and repositories
#ExtendWith(SpringExtension.class)
#DataJpaTest
public class ExpeditionServiceTest {
#Autowired
private ArticleRepository articleRepository;
#Autowired
private TypeAdresseRespository typeAdresseRespository;
#Autowired
private AdressRepository adressRepository;
#Autowired
OrderRepository orderRepository;
#Autowired
CustomerRepository customerRepository;
#Autowired
CountryRepository countryRepository;
ExpeditionService expeditionService = new ExpeditionService(articleRepository , customerRepository , adressRepository);
}
But when I launch test on no matter what kind of method I'm getting error that this repository is null
What should I do please?

Please rewrite your unit test case like this and try again.
#ExtendWith(MockitoExtension.class)
public class ExpeditionServiceTest {
#Mock
private ArticleRepository articleRepository;
#Mock
private AdressRepository adressRepository;
#Mock
CustomerRepository customerRepository;
ExpeditionService expeditionService;
#BeforeEach
void setUp(){
expeditionService = new ExpeditionService(articleRepository , customerRepository , adressRepository);
}
#Test
void test1() {
//test code
}
}

Related

How do I write a test for a service that uses other services in Springboot?

I have a BookManagementService that uses the #Autowired implementation of three other services, like so
#Service
public class BookManagementService {
private final BookRepo repo;
private final BookItemRepo itemRepo;
private final BookEditionRepo editionRepo;
// the below services are the ones I want to mock in the test.
#Autowired AuthorService authorService;
#Autowired YearService yearService;
#Autowired GenreService genreService;
static String position = "1000";
public BookManagementService(BookRepo repo, BookItemRepo itemRepo, BookEditionRepo editionRepo,
YearService yearService) {
this.repo = repo;
this.itemRepo = itemRepo;
this.editionRepo = editionRepo;
}
// Rest of the methods that perform the business logic.
}
So how do I mock the aforementioned services and their repo in the BookManagementServiceTest?
When the test start running and it gets to the yearService layer, it throws a NullPointerEXception cause the year it receives is null
The BookManagementServiceTest
#SpringBootTest
#ExtendWith(MockitoExtension.class)
class BookManagementServiceTest {
// Injects the needed services
#InjectMocks private BookManagementService service;
#InjectMocks private YearService yearService;
#InjectMocks private GenreService genreService;
#InjectMocks private AuthorService authorService;
// Mock the needed repos
#Mock private BookItemRepo repoItem;
#Mock private BookEditionRepo repoEdition;
#Mock private BookRepo repo;
// External repo
#Mock private BookYearRepo yearRepo;
#Mock private GenreRepo genreRepo;
#Mock private AuthorRepo authorRepo;
#BeforeEach
void setUp() {
// instantiate the injected services
service = new BookManagementService(repo, repoItem, repoEdition, yearService);
yearService = new YearService(yearRepo);
genreService = new GenreService(genreRepo);
authorService = new AuthorService(authorRepo);
// setting the needed variables
// calling when.thenReturn for all the repos like I would in a normal single class test.
lenient().when(yearRepo.findByYear("2006")).thenReturn(year);
lenient().when(yearRepo.save(year)).thenReturn(year);
lenient().when(repoItem.save(item)).thenReturn(item);
lenient().when(yearService.create("2006", edition)).thenReturn(year);
lenient().when(repoEdition.save(edition)).thenReturn(edition);
lenient().when(repo.save(book)).thenReturn(book);
}
// Tests
}
You just have to mock all the related services aswell.
Take this as an example, imagine you want to test an ItemService class, that has ItemRepositoy and AuthService autowired.
ItemService.java
#Service
public class ItemService {
#Autowired
private ItemRepository itemRepository;
#Autowired
private AuthService authService;
public Item fetchItem() {
return new Item(1, "name", 10, 100);
}
public List<Item> fetchItems() {
List<Item> items = itemRepository.findAll();
Boolean isValidItems = authService.checkItems();
if (isValidItems) items.forEach((item) -> item.setValue(item.getPrice() * item.getQuantity()));
return items;
}
}
Item.java
#Entity
public class Item {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private Integer price;
private Integer quantity;
// getters, setters and constructors...
}
AuthService.java
#Service
public class AuthService {
public Boolean checkItems() {
return true;
}
}
ItemRepository.java
public interface ItemRepository extends JpaRepository<Item, Integer> {
}
ItemServiceTest.java
#ExtendWith(MockitoExtension.class)
public class ItemServiceTest {
#InjectMocks
private ItemService itemServiceMock;
#Mock
private ItemRepository itemRepositoryMock;
#Mock
private AuthService authServiceMock;
#Test
public void fetchItems_basic() {
// arrange
List<Item> items = Arrays.asList(new Item(1, "first", 10, 100), new Item(2, "second", 20, 200));
Integer expectedResultFirst = 1000;
Integer expectedResultSecond = 4000;
when(itemRepositoryMock.findAll()).thenReturn(items);
when(authServiceMock.checkItems()).thenReturn(true);
// act
List<Item> actualResult = itemServiceMock.fetchItems();
// assert
assertEquals(expectedResultFirst, actualResult.get(0).getValue());
assertEquals(expectedResultSecond, actualResult.get(1).getValue());
}
}
If you don't mock the autowired classes and set the when().thenReturn() as you expect you will always get a NullPointException.

Why can't I mock service dependency of controller when trying to test it using mockMvc?

I'm new to testing controller and web layer.
I have Service which has methods to work with orders, and I have controller that has that service as dependency
OrderService:
#Service
public class OrderServiceImpl implements OrderService{
private OrderRepository orderRepository;
private Assembler<Order, OrderDto> orderDtoAssembler;
#Autowired
public OrderServiceImpl(OrderRepository orderRepository,Assembler<Order, OrderDto> orderDtoAssembler) {
this.orderRepository = orderRepository;
this.orderDtoAssembler = orderDtoAssembler;
}
#Override
#Transactional
public List<OrderDto> findFilSortOrders(Map<String, String> filters, String sortedColumn, boolean descending, int startRow, int rowsPerPage) {
return orderRepository.findFilSortOrders(filters, sortedColumn, descending, startRow, rowsPerPage)
.stream().map(orderDtoAssembler::mergeAggregateIntoDto).collect(Collectors.toList());
}
}
And Controller has several mapping and the one I want to test.
OrderController :
#Controller
public class OrderController {
private static final Logger LOGGER = LogManager.getLogger(OrderController.class);
private final CarCategoryService carCategoryService;
private final TaxiServiceMakeOrder serviceMakeOrder;
private final OrderService orderService;
private final UserService userService;
#Autowired
public OrderController(CarCategoryService carCategoryService, TaxiServiceMakeOrder serviceMakeOrder, OrderService orderService, UserService userService) {
this.carCategoryService = carCategoryService;
this.serviceMakeOrder = serviceMakeOrder;
this.orderService = orderService;
this.userService = userService;
}
#GetMapping("/admin/ordersJson")
#ResponseStatus(HttpStatus.OK)
#ResponseBody
public List<OrderDto> getOrders(#RequestParam String sortBy,
#RequestParam String filter,
#RequestParam boolean descending,
#RequestParam int startRow,
#RequestParam int rowsPerPage) throws JsonProcessingException {
LOGGER.info("OrdersGetAction is invoked");
ObjectMapper mapper = new ObjectMapper();
Map<String, String> filters = null;
if (!filter.equals("{}")) {
filters = mapper.readValue(filter, new TypeReference<Map<String, String>>() {
});
}
List<OrderDto> getOrders = orderService.findFilSortOrders(filters, sortBy, descending, startRow, rowsPerPage);
System.out.println("Controller :"+getOrders.size());;//to check If it works
LOGGER.info("Filtered and Sorted Order count is {}", getOrders.size());
return getOrders;
}
}
My test util to generate dto :
public class OrderDtoUtil {
public static List<OrderDto> generateOrderDto(int count) {
return IntStream.range(0, count)
.mapToObj(i -> {
OrderDto orderDto = new OrderDto();
orderDto.setOrderId((long) i);
orderDto.setOrderDate(LocalDateTime.now());
orderDto.setOrderCost(500);
orderDto.setUserDestination("UserDestination");
orderDto.setUserAddress("UserAddress");
orderDto.setUserId((long) i);
orderDto.setCarId((long) i);
return orderDto;
}).collect(Collectors.toList());
}
}
My test class for controller :
class OrderControllerTest {
private CarCategoryService carCategoryService;
private TaxiServiceMakeOrder serviceMakeOrder;
private OrderService orderService;
private UserService userService;
private OrderController orderController;
private MockMvc mockMvc;
#BeforeEach
public void setUp() {
carCategoryService = mock(CarCategoryService.class);
serviceMakeOrder = mock(TaxiServiceMakeOrder.class);
orderService = mock(OrderService.class);
userService = mock(UserService.class);
orderController = new OrderController(carCategoryService, serviceMakeOrder, orderService, userService);
mockMvc = standaloneSetup(orderController)
.defaultRequest(get("/")
.contextPath("/petProject_war_exploded")
.accept(MediaType.ALL)).build();
}
#Test
void getOrders() throws Exception {
List<OrderDto> orderDtos = OrderDtoUtil.generateOrderDto(3);
System.out.println("TEST method :"+orderDtos.size());
when(orderService.findFilSortOrders(anyMap(), anyString(), anyBoolean(), anyInt(), anyInt())).thenReturn(orderDtos);
mockMvc.perform(get("/petProject_war_exploded/admin/ordersJson")
.param("filter", "{}")
.param("sortBy", "orderId")
.param("descending", "false")
.param("startRow", "0")
.param("rowsPerPage", "3"))
.andExpect(status().isOk())
.andExpect(jsonPath("$", hasSize(3)))
.andExpect(jsonPath("$[0].orderId", is(orderDtos.get(0).getOrderId())))
.andExpect(jsonPath("$[1].orderId", is(orderDtos.get(1).getOrderId())))
.andExpect(jsonPath("$[2].orderId", is(orderDtos.get(2).getOrderId())));
}
}
And It fails on hasSize method because It shows that size is 0.
Result :
Can you explain why does it happen?

Java Spring Boot: Dependency Injection error: Change 1st parameter of abstract method to Optional

I am trying to use dependency injection in Java Spring Boot. Receiving Error below in controller in this line. productService.updateProduct(product);
Error: Change 1st parameter of abstract method from Product to Optional
How can this be resolved?
public class Product {
#Getter #Setter public #Id #GeneratedValue Long productId;
#Getter #Setter public String productName;
#Getter #Setter public String productDescription;
Product() {}
Product(String productName, String productDescription) {
this.productName = productName;
this.productDescription = productDescription;
}
}
Interface:
public interface IProductService {
public Product updateProduct(Product product);
}
Service:
#Service
public class ProductService implements IProductService {
#Override
public Product updateProduct(Product product){
product.productName = "test12345";
return product;
}
}
Controller:
class ProductController {
ProductRepository repository;
#Autowired IProductService productService;
ProductController(IProductService productService, ProductRepository repository) {
this.productService = productService;
this.repository = repository;
}
#GetMapping("/products/{id}")
Product one(#PathVariable Long id) {
var product = repository.findById(id);
var finalProduct = productService.updateProduct(product); // error in this line
return finalProduct;
}
And check whether your Repository class return Optional<Product>. because findbyId return type of optional. Then your have to use like below.
Optional<Product> product = repository.findById(id);
And Better to add #Autowire #Controller annotation no need constructor.
#Controller
class ProductController {
#Autowired
private ProductRepository repository;
#Autowire
private IProductService productService;
}

MockHttpServletResponse body empty

I try to test my method addPerson() in my controller, but when I execute the test I have status 200 with an empty body in MockHttpServletResponse. I would like to test the body response with jsonPath from MockMvcResultMatchers but I can't do it while the body is empty.
Here is my test:
#WebMvcTest(PersonController.class)
#ExtendWith(SpringExtension.class)
public class PersonControllerTest {
#Autowired
private MockMvc mvc;
#Autowired
private Model model;
#MockBean
private PersonService service;
#Test
public void addPersonTest() throws Exception {
this.mvc.perform(post("/person/add")
.contentType(MediaType.APPLICATION_JSON).content("{\"firstName\": \"Test\",\"lastName\": \"\",\"address\": \"\",\"city\": \"\",\"zip\": \"\",\"phone\": \"\",\"email\": \"\"}"))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk());
}
Here is my controller with the method addPerson()
#RequestMapping("/person")
#RestController
public class PersonController {
#Autowired
Model model;
private static final Logger logger = LogManager.getRootLogger();
#Autowired
private PersonService personService;
#GetMapping("/")
public List<Person> allPerson() {
return personService.all();
}
#PostMapping("/add")
public List<Person> addPerson(#RequestBody Person person) {
List<Person> listPerson = this.personService.add(person);
logger.info("Request = #RequestBody = {}", person);
logger.info("Response {}", listPerson);
return listPerson;
}
And here is the service:
#Service
public class PersonService {
#Autowired
private Model model;
public PersonService(Model model2) {
this.model = model2;
}
public List<Person> add(Person person) {
List<Person> listPersons = model.getPersons();
listPersons.add(person);
return listPersons;
}
Thanks for your help.
As you mock the PersonService you have to provide its behaviour otherwise it always returns null. You can use when().thenReturn() from Mockito for this:
#WebMvcTest(PersonController.class)
#ExtendWith(SpringExtension.class)
public class PersonControllerTest {
#Autowired
private MockMvc mvc;
#Autowired
private Model model;
#MockBean
private PersonService service;
#Test
public void addPersonTest() throws Exception {
List<Person> personList = Arrays.asList(new Person()); // create list here
when(service.add(any(Person.class)).thenReturn(personList); // mock the behaviour of your PersonService bean
this.mvc.perform(post("/person/add")
.contentType(MediaType.APPLICATION_JSON).content("{\"firstName\": \"Test\",\"lastName\": \"\",\"address\": \"\",\"city\": \"\",\"zip\": \"\",\"phone\": \"\",\"email\": \"\"}"))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk());
}
}

Spring Data REST Event not raised in #RepositoryRestController

I'm using Spring Boot, Spring Data REST, Spring HATEOAS. I created a #RepositoryRestController:
#Api(tags = "Ticket Entity")
#RepositoryRestController
#PreAuthorize("isAuthenticated()")
public class TicketController extends RevisionController<TransitCertificate> {
private Logger log = LogManager.getLogger();
#Autowired
private LocalValidatorFactoryBean validator;
#Autowired
private TicketService ticketService;
#Autowired
private EnumTranslator enumTranslator;
#SuppressWarnings("rawtypes")
#Autowired
private PagedResourcesAssembler pagedResourcesAssembler;
#Autowired
private MessageSource messageSource;
#Autowired
private Javers javers;
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.addValidators(validator);
}
#PostMapping(path = "/tickets")
public ResponseEntity<?> save(#RequestBody(required = true) #Valid Ticket ticket, PersistentEntityResourceAssembler resourceAssembler) {
return new ResponseEntity<>(resourceAssembler.toResource(ticketService.save(ticket)), HttpStatus.OK);
}
}
I need to intercept the event before the Ticket object is persisted. I created my handler:
#Component
#RepositoryEventHandler(Ticket.class)
public class TicketHandler {
private Logger log = LogManager.getLogger();
#Autowired
private WorkShiftRepository workShiftRepository;
#HandleBeforeCreate
public void handleBeforeCreates(Ticket ticket) {
WorkShift workShift = workShiftRepository.findByAgentUsernameAndEndDateIsNull();
if (workShift != null) {
ticket.setWorkShift(workShift);
}
}
}
and this is my TicketRepository:
#Transactional
#PreAuthorize("isAuthenticated()")
public interface TicketRepository extends PagingAndSortingRepository<Ticket, Long> {
#RestResource(exported = false)
#Override
public <S extends Ticket> Iterable<S> save(Iterable<S> entities);
#RestResource(exported = false)
#Override
public <S extends Ticket> S save(S entity);
#RestResource(exported = false)
#Override
public void delete(Long id);
#RestResource(exported = false)
#Override
public void delete(Ticket entity);
#Query(value = "SELECT MAX(number) FROM Ticket t WHERE t.block=:ticketBlock")
public Long findMaxNumber(#Param("ticketBlock") TicketBlock ticketBlock);
}
as described in the documentation but the event is not emitted. Like described here I'm using the #HandleBeforeCreate annotation. Am I doing something wrong?

Categories

Resources