From 5c18b2e7f7b84daae9dc4687ca11238afacd67ed Mon Sep 17 00:00:00 2001 From: Tetiana Okhotnik Date: Sun, 3 Mar 2024 23:35:32 +0200 Subject: [PATCH] BAEL-7238 Inject mock into Spy object in Mockito - added "library" service to demonstrate mockito injection features - updated mockito version to 5.10 --- testing-modules/mockito-2/pom.xml | 9 +++- .../com/baeldung/injectmockintospy/Book.java | 21 +++++++++ .../injectmockintospy/BookControlService.java | 33 +++++++++++++ .../injectmockintospy/BookStorageService.java | 28 +++++++++++ .../injectmockintospy/RepairService.java | 12 +++++ .../injectmockintospy/StatisticService.java | 22 +++++++++ ...MockAndManualSpyWithExtensionUnitTest.java | 32 +++++++++++++ ...MockAndManualSpyWithOpenMocksUnitTest.java | 46 +++++++++++++++++++ ...tMockAndMockitoSpyDoesnotWorkUnitTest.java | 36 +++++++++++++++ ...ngleInjectMockAndAnnotatedSpyUnitTest.java | 33 +++++++++++++ ...ockAndManualSpyWithReflectionUnitTest.java | 46 +++++++++++++++++++ 11 files changed, 317 insertions(+), 1 deletion(-) create mode 100644 testing-modules/mockito-2/src/main/java/com/baeldung/injectmockintospy/Book.java create mode 100644 testing-modules/mockito-2/src/main/java/com/baeldung/injectmockintospy/BookControlService.java create mode 100644 testing-modules/mockito-2/src/main/java/com/baeldung/injectmockintospy/BookStorageService.java create mode 100644 testing-modules/mockito-2/src/main/java/com/baeldung/injectmockintospy/RepairService.java create mode 100644 testing-modules/mockito-2/src/main/java/com/baeldung/injectmockintospy/StatisticService.java create mode 100644 testing-modules/mockito-2/src/test/java/com/baeldung/injectmockintospy/MultipleInjectMockAndManualSpyWithExtensionUnitTest.java create mode 100644 testing-modules/mockito-2/src/test/java/com/baeldung/injectmockintospy/MultipleInjectMockAndManualSpyWithOpenMocksUnitTest.java create mode 100644 testing-modules/mockito-2/src/test/java/com/baeldung/injectmockintospy/MultipleInjectMockAndMockitoSpyDoesnotWorkUnitTest.java create mode 100644 testing-modules/mockito-2/src/test/java/com/baeldung/injectmockintospy/SingleInjectMockAndAnnotatedSpyUnitTest.java create mode 100644 testing-modules/mockito-2/src/test/java/com/baeldung/injectmockintospy/SingleInjectMockAndManualSpyWithReflectionUnitTest.java 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); + } +}