I tried to test the FetchById method in my BookServiceTest using Mockito but this error occurred
I’m using JUnit 5 and Mockito, that come along with spring boot starter test in spring boot version 3.1.3
the error:
org.opentest4j.AssertionFailedError: expected: not <null>
at org.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:152)
at org.junit.jupiter.api.AssertionFailureBuilder.buildAndThrow(AssertionFailureBuilder.java:132)
at org.junit.jupiter.api.AssertNotNull.failNull(AssertNotNull.java:49)
at org.junit.jupiter.api.AssertNotNull.assertNotNull(AssertNotNull.java:35)
at org.junit.jupiter.api.AssertNotNull.assertNotNull(AssertNotNull.java:30)
at org.junit.jupiter.api.Assertions.assertNotNull(Assertions.java:301)
at com.bookstore.com.serviceLevelTest.BookServTest.isFetchByIdValid(BookServTest.java:123)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
at java.base/java.lang.reflect.Method.invoke(Method.java:578)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727)
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:156)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:147)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:86)
at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103)
at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93)
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.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92)
at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:217)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:213)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:138)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:147)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:127)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:90)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:55)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:102)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:54)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:57)
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:232)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)
Here is my test class:
@ExtendWith(MockitoExtension.class)
public class BookServTest {
private static final Logger logger = LoggerFactory.getLogger(BookServTest.class);
@Mock
private BookRepository bookRepository;
@Mock
private ModelMappers modelMapper;
@Mock
AuthorService authorService;
private BookService bookService;
//Mock Object
List<Book> bookDtos;
private Book book;
private BookDto bookDto;
private Author author;
@BeforeEach
public void setUp(){
bookService = new BookService(modelMapper,bookRepository,authorService);
//Create BookDto
bookDto = new BookDto();
bookDto.setId(1L);
bookDto.setTitle("The Usual Suspect");
bookDto.setCategory("Crime");
bookDto.setPrice(7800.55);
bookDto.setAuthorName("Somxai");
//Create Book
book = new Book();
book.setId(1L);
book.setTitle("The Usual Suspect");
book.setCategory("Crime");
book.setPrice(7800.55);
//Create list of bookDto
bookDtos = new ArrayList<>();
bookDtos.add(new Book(1L, "The Usual Suspect","Crime",7800.50));
bookDtos.add(new Book(2L, "The Shutter Island","Crime",9000.50));
bookDtos.add(new Book(3L, "The Shutter Island","Crime",9000.50));
//Create Author
author = new Author("Somxai","SSM","Laos PDR");
}
//The error method:
@Test
void isFetchByIdValid(){
//Arrange
Mockito.when(bookRepository.fetchById(1L)).thenReturn(Optional.of(book)); //as i have checked this method is ok
//Act
BookDto bookDTO = bookService.fetchById(1L); // bookDTO return null
//Assert
Assertions.assertNotNull(bookDTO);
}
//this method work fine
@Test
void isFetchAllValid(){
//Arrange
Pageable pageable = PageRequest.of(0,3,Sort.by("title").descending());
Page<Book> expectedPage = new PageImpl<>(bookDtos, pageable, bookDtos.size());
Mockito.when(bookRepository.findAll(pageable)).thenReturn(expectedPage);
//Act
List<BookDto> bookDtos1 = bookService.fetchAll(0,3,"title");
//Assertion
Assertions.assertNotNull(bookDtos1);
Assertions.assertEquals(3,bookDtos1.size());
}
Here is service method:
@Transactional
@Cacheable(value = "book", key = "#id")
public BookDto fetchById(Long id) {
Optional<Book> book = bookRepository.fetchById(id);
if (book.isEmpty()) {
logger.info("book not found");
}
Book book1 = book.get();
return modelMapper.bookToDTO(book1);
}
Here is repository method:
@Transactional
@Query(value = "SELECT b FROM Book b JOIN FETCH b.author a WHERE b.id = :id")
Optional<Book> fetchById(@Param("id") Long id);
Here is mapping method from model mapper:
public BookDto bookToDTO(Book book){
BookDto bookDto = new BookDto();
bookDto = modelMapper().map(book, BookDto.class);
if (book.getAuthor() != null){
bookDto.setAuthorName(book.getAuthor().getFirstName() + " " + book.getAuthor().getLastName());
}
return bookDto;
}
I have been searching for a similar issue and tried many approaches but not work for me.
note: When I tested in Postman, every method working fine.
Thanks in advance.
>Solution :
The issue you are encountering where bookDTO is null in your isFetchByIdValid test is likely due to a problem in your mock setup or how the modelMapper is used. Here are some steps to troubleshoot and resolve the issue:
-
Ensure the
bookRepository.fetchById(1L)mock setup is correct:In your test, you have this line:
Mockito.when(bookRepository.fetchById(1L)).thenReturn(Optional.of(book));Make sure that the
bookRepositoryis properly mocked and that thefetchByIdmethod is being called with1L. Also, ensure that thebookobject being returned is correctly set up and not null. -
Verify the
modelMapper:Check your
modelMapperconfiguration and ensure that it is correctly mapping aBookentity to aBookDto. The issue might be with how themodelMapperis set up or with the mapping logic itself. -
Debugging:
You can add some debug statements to your test and service method to help diagnose the issue. For example, you can print the values of
book,book1, andbookDTOin your test to see where the null value is coming from.@Test void isFetchByIdValid() { // Arrange Mockito.when(bookRepository.fetchById(1L)).thenReturn(Optional.of(book)); // Act BookDto bookDTO = bookService.fetchById(1L); // Debugging System.out.println("book: " + book); System.out.println("book1: " + book1); System.out.println("bookDTO: " + bookDTO); // Assert Assertions.assertNotNull(bookDTO); }In your service method, you can add log statements to check if
bookis being fetched and if the mapping tobookDTOis successful:@Transactional @Cacheable(value = "book", key = "#id") public BookDto fetchById(Long id) { Optional<Book> book = bookRepository.fetchById(id); if (book.isEmpty()) { logger.info("Book not found"); } else { logger.info("Book found: " + book.get()); // Log the book object } Book book1 = book.get(); logger.info("Mapping book to bookDTO..."); BookDto bookDTO = modelMapper.bookToDTO(book1); logger.info("Mapped book to bookDTO: " + bookDTO); // Log the bookDTO object return bookDTO; }This should help you pinpoint where the issue is occurring.
-
Check the
modelMapperconfiguration:Ensure that the
modelMapperis correctly configured to map aBookentity to aBookDto. Double-check that the mapping logic is correct and that it’s not causing any issues.
By following these steps, you should be able to identify and resolve the issue causing bookDTO to be null in your test. It’s likely related to either the mock setup, the modelMapper configuration, or how the mapping is performed in your service method.