diff --git a/testing-modules/mockito-2/pom.xml b/testing-modules/mockito-2/pom.xml
index 9e9d208266..79b8c86221 100644
--- a/testing-modules/mockito-2/pom.xml
+++ b/testing-modules/mockito-2/pom.xml
@@ -24,11 +24,18 @@
${mockito.version}
test
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+ compile
+
4.8.1
- 5.9.0
+ 5.10.0
+ 1.18.30
\ No newline at end of file
diff --git a/testing-modules/mockito-2/src/main/java/com/baeldung/injectmockintospy/Book.java b/testing-modules/mockito-2/src/main/java/com/baeldung/injectmockintospy/Book.java
new file mode 100644
index 0000000000..0bd06e9ea2
--- /dev/null
+++ b/testing-modules/mockito-2/src/main/java/com/baeldung/injectmockintospy/Book.java
@@ -0,0 +1,21 @@
+package com.baeldung.injectmockintospy;
+
+import java.time.ZonedDateTime;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+@Data
+@AllArgsConstructor
+public class Book {
+ private String name;
+ private String author;
+ private long timesTaken;
+ private ZonedDateTime returnDate;
+
+ public Book(String name, String author, long timesTaken) {
+ this.name = name;
+ this.author = author;
+ this.timesTaken = timesTaken;
+ }
+}
diff --git a/testing-modules/mockito-2/src/main/java/com/baeldung/injectmockintospy/BookControlService.java b/testing-modules/mockito-2/src/main/java/com/baeldung/injectmockintospy/BookControlService.java
new file mode 100644
index 0000000000..1ccdde329e
--- /dev/null
+++ b/testing-modules/mockito-2/src/main/java/com/baeldung/injectmockintospy/BookControlService.java
@@ -0,0 +1,33 @@
+package com.baeldung.injectmockintospy;
+
+import java.time.ZonedDateTime;
+
+import lombok.NoArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@NoArgsConstructor
+public class BookControlService {
+
+ private StatisticService statisticService;
+ private RepairService repairService;
+
+ public void returnBook(Book book) {
+ book.setReturnDate(null);
+ statisticService.calculateAdded();
+ if(repairService.shouldRepair(book)){
+ log.info("Book should be repaired");
+ }
+ }
+
+ public void giveBook(Book book) {
+ book.setReturnDate(ZonedDateTime.now().plusDays(14L));
+ statisticService.calculateRemoved();
+ }
+
+ public BookControlService(StatisticService statisticService, RepairService repairService) {
+ this.statisticService = statisticService;
+ this.repairService = repairService;
+ }
+
+}
diff --git a/testing-modules/mockito-2/src/main/java/com/baeldung/injectmockintospy/BookStorageService.java b/testing-modules/mockito-2/src/main/java/com/baeldung/injectmockintospy/BookStorageService.java
new file mode 100644
index 0000000000..993b54b33b
--- /dev/null
+++ b/testing-modules/mockito-2/src/main/java/com/baeldung/injectmockintospy/BookStorageService.java
@@ -0,0 +1,28 @@
+package com.baeldung.injectmockintospy;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import lombok.Getter;
+
+@Getter
+public class BookStorageService {
+
+ private BookControlService bookControlService;
+ private final List availableBooks = new LinkedList<>();
+
+ public BookStorageService(BookControlService bookControlService) {
+ this.bookControlService = bookControlService;
+ }
+
+ public void returnBook(Book book) {
+ availableBooks.add(book);
+ bookControlService.returnBook(book);
+ }
+
+ public void giveBook(Book book) {
+ availableBooks.remove(book);
+ bookControlService.giveBook(book);
+ }
+
+}
diff --git a/testing-modules/mockito-2/src/main/java/com/baeldung/injectmockintospy/RepairService.java b/testing-modules/mockito-2/src/main/java/com/baeldung/injectmockintospy/RepairService.java
new file mode 100644
index 0000000000..254b26d7dd
--- /dev/null
+++ b/testing-modules/mockito-2/src/main/java/com/baeldung/injectmockintospy/RepairService.java
@@ -0,0 +1,12 @@
+package com.baeldung.injectmockintospy;
+
+import lombok.Getter;
+
+@Getter
+public class RepairService {
+
+ public boolean shouldRepair(Book book) {
+ return book.getTimesTaken() > 10;
+ }
+
+}
diff --git a/testing-modules/mockito-2/src/main/java/com/baeldung/injectmockintospy/StatisticService.java b/testing-modules/mockito-2/src/main/java/com/baeldung/injectmockintospy/StatisticService.java
new file mode 100644
index 0000000000..e35a7054e9
--- /dev/null
+++ b/testing-modules/mockito-2/src/main/java/com/baeldung/injectmockintospy/StatisticService.java
@@ -0,0 +1,22 @@
+package com.baeldung.injectmockintospy;
+
+import lombok.Getter;
+
+@Getter
+public class StatisticService {
+
+ private long todaysActions;
+
+ public void calculateAdded() {
+ todaysActions++;
+ }
+
+ public void calculateRemoved() {
+ todaysActions--;
+ }
+
+ public void reset() {
+ todaysActions = 0L;
+ }
+
+}
diff --git a/testing-modules/mockito-2/src/test/java/com/baeldung/injectmockintospy/MultipleInjectMockAndManualSpyWithExtensionUnitTest.java b/testing-modules/mockito-2/src/test/java/com/baeldung/injectmockintospy/MultipleInjectMockAndManualSpyWithExtensionUnitTest.java
new file mode 100644
index 0000000000..8ff6ea9fe0
--- /dev/null
+++ b/testing-modules/mockito-2/src/test/java/com/baeldung/injectmockintospy/MultipleInjectMockAndManualSpyWithExtensionUnitTest.java
@@ -0,0 +1,32 @@
+package com.baeldung.injectmockintospy;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+public class MultipleInjectMockAndManualSpyWithExtensionUnitTest {
+ @InjectMocks
+ private BookStorageService bookStorageService;
+ @InjectMocks
+ private BookControlService bookControlService = Mockito.spy(BookControlService.class);
+ @Mock
+ private StatisticService statisticService;
+ @Mock
+ private RepairService repairService;
+
+ @Test
+ void whenInjectMockUsedWithManualSpy_thenMockitoCanInjectMocks() {
+ Book book = new Book("Some name", "Some author", 355);
+ bookStorageService.returnBook(book);
+
+ Assertions.assertEquals(1, bookStorageService.getAvailableBooks().size());
+ Mockito.verify(bookControlService).returnBook(book);
+ Mockito.verify(statisticService).calculateAdded();
+ Mockito.verify(repairService).shouldRepair(book);
+ }
+}
diff --git a/testing-modules/mockito-2/src/test/java/com/baeldung/injectmockintospy/MultipleInjectMockAndManualSpyWithOpenMocksUnitTest.java b/testing-modules/mockito-2/src/test/java/com/baeldung/injectmockintospy/MultipleInjectMockAndManualSpyWithOpenMocksUnitTest.java
new file mode 100644
index 0000000000..8e71b84c64
--- /dev/null
+++ b/testing-modules/mockito-2/src/test/java/com/baeldung/injectmockintospy/MultipleInjectMockAndManualSpyWithOpenMocksUnitTest.java
@@ -0,0 +1,46 @@
+package com.baeldung.injectmockintospy;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+public class MultipleInjectMockAndManualSpyWithOpenMocksUnitTest {
+
+ @InjectMocks
+ private BookStorageService bookStorageService;
+ @InjectMocks
+ private BookControlService bookControlService;
+ @Mock
+ private StatisticService statisticService;
+ @Mock
+ private RepairService repairService;
+ private AutoCloseable closeable;
+
+ @BeforeEach
+ public void openMocks() {
+ bookControlService = Mockito.spy(BookControlService.class);
+ closeable = MockitoAnnotations.openMocks(this);
+ }
+
+ @AfterEach
+ public void releaseMocks() throws Exception {
+ closeable.close();
+ }
+
+ @Test
+ void whenInjectMockUsedWithManualSpy_thenMockitoCanInjectMocks() {
+ Book book = new Book("Some name", "Some author", 355);
+ bookStorageService.returnBook(book);
+
+ Assertions.assertEquals(1, bookStorageService.getAvailableBooks().size());
+
+ Mockito.verify(bookControlService).returnBook(book);
+ Mockito.verify(statisticService).calculateAdded();
+ Mockito.verify(repairService).shouldRepair(book);
+ }
+}
\ No newline at end of file
diff --git a/testing-modules/mockito-2/src/test/java/com/baeldung/injectmockintospy/MultipleInjectMockAndMockitoSpyDoesnotWorkUnitTest.java b/testing-modules/mockito-2/src/test/java/com/baeldung/injectmockintospy/MultipleInjectMockAndMockitoSpyDoesnotWorkUnitTest.java
new file mode 100644
index 0000000000..d522ebf01d
--- /dev/null
+++ b/testing-modules/mockito-2/src/test/java/com/baeldung/injectmockintospy/MultipleInjectMockAndMockitoSpyDoesnotWorkUnitTest.java
@@ -0,0 +1,36 @@
+package com.baeldung.injectmockintospy;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.Spy;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+public class MultipleInjectMockAndMockitoSpyDoesnotWorkUnitTest {
+ @InjectMocks
+ private BookStorageService bookStorageService;
+ @InjectMocks
+ @Spy
+ private BookControlService bookControlService;
+ @Mock
+ private StatisticService statisticService;
+ @Mock
+ private RepairService repairService;
+
+ @Test
+ @Disabled("test is not intended to work, it is here to show that such mock setup doesn't work")
+ void whenInjectMockUsedWithMockitoSpy_thenMockitoCannotInjectObjectProperly() {
+ Book book = new Book("Some name", "Some author", 355);
+ bookStorageService.returnBook(book);
+
+ Assertions.assertEquals(1, bookStorageService.getAvailableBooks().size());
+ Mockito.verify(bookControlService).returnBook(book);
+ Mockito.verify(statisticService).calculateAdded();
+ Mockito.verify(repairService).shouldRepair(book);
+ }
+}
diff --git a/testing-modules/mockito-2/src/test/java/com/baeldung/injectmockintospy/SingleInjectMockAndAnnotatedSpyUnitTest.java b/testing-modules/mockito-2/src/test/java/com/baeldung/injectmockintospy/SingleInjectMockAndAnnotatedSpyUnitTest.java
new file mode 100644
index 0000000000..abd1075d86
--- /dev/null
+++ b/testing-modules/mockito-2/src/test/java/com/baeldung/injectmockintospy/SingleInjectMockAndAnnotatedSpyUnitTest.java
@@ -0,0 +1,33 @@
+package com.baeldung.injectmockintospy;
+
+import java.time.ZonedDateTime;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.Spy;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+public class SingleInjectMockAndAnnotatedSpyUnitTest {
+ @Spy
+ @InjectMocks
+ private BookControlService bookControlService;
+ @Mock
+ private StatisticService statisticService;
+ @Spy
+ private RepairService repairService;
+
+ @Test
+ void whenOneInjectMockWithSpy_thenHierarchySuccessfullyInitialized() {
+ Book book = new Book("Some name", "Some author", 355, ZonedDateTime.now());
+ bookControlService.returnBook(book);
+
+ Assertions.assertNull(book.getReturnDate());
+ Mockito.verify(statisticService).calculateAdded();
+ Mockito.verify(repairService).shouldRepair(book);
+ }
+}
diff --git a/testing-modules/mockito-2/src/test/java/com/baeldung/injectmockintospy/SingleInjectMockAndManualSpyWithReflectionUnitTest.java b/testing-modules/mockito-2/src/test/java/com/baeldung/injectmockintospy/SingleInjectMockAndManualSpyWithReflectionUnitTest.java
new file mode 100644
index 0000000000..f8e697e7d6
--- /dev/null
+++ b/testing-modules/mockito-2/src/test/java/com/baeldung/injectmockintospy/SingleInjectMockAndManualSpyWithReflectionUnitTest.java
@@ -0,0 +1,46 @@
+package com.baeldung.injectmockintospy;
+
+import java.lang.reflect.Field;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+public class SingleInjectMockAndManualSpyWithReflectionUnitTest {
+ @InjectMocks
+ private BookStorageService bookStorageService;
+ @Mock
+ private StatisticService statisticService;
+ @Mock
+ private RepairService repairService;
+ private BookControlService bookControlService;
+
+ @BeforeEach
+ public void openMocks() throws Exception {
+ bookControlService = Mockito.spy(new BookControlService(statisticService, repairService));
+ injectSpyToTestedMock(bookStorageService, bookControlService);
+ }
+
+ private void injectSpyToTestedMock(BookStorageService bookStorageService, BookControlService bookControlService) throws NoSuchFieldException, IllegalAccessException {
+ Field bookControlServiceField = BookStorageService.class.getDeclaredField("bookControlService");
+ bookControlServiceField.setAccessible(true);
+ bookControlServiceField.set(bookStorageService, bookControlService);
+ }
+
+ @Test
+ void whenManualSpyInjectedToTestesClass_thenHierarchySuccessfullyInitialized() {
+ Book book = new Book("Some name", "Some author", 355);
+ bookStorageService.returnBook(book);
+
+ Assertions.assertEquals(1, bookStorageService.getAvailableBooks().size());
+ Mockito.verify(bookControlService).returnBook(book);
+ Mockito.verify(statisticService).calculateAdded();
+ Mockito.verify(repairService).shouldRepair(book);
+ }
+}