BAEL-7238 Inject mock into Spy object in Mockito

- added "library" service to demonstrate mockito injection features
- updated mockito version to 5.10
This commit is contained in:
Tetiana Okhotnik 2024-03-03 23:35:32 +02:00
parent 4f0a8ce5d9
commit 5c18b2e7f7
11 changed files with 317 additions and 1 deletions

View File

@ -24,11 +24,18 @@
<version>${mockito.version}</version> <version>${mockito.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>compile</scope>
</dependency>
</dependencies> </dependencies>
<properties> <properties>
<mockito-inline.version>4.8.1</mockito-inline.version> <mockito-inline.version>4.8.1</mockito-inline.version>
<mockito.version>5.9.0</mockito.version> <mockito.version>5.10.0</mockito.version>
<lombok.version>1.18.30</lombok.version>
</properties> </properties>
</project> </project>

View File

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

View File

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

View File

@ -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<Book> 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);
}
}

View File

@ -0,0 +1,12 @@
package com.baeldung.injectmockintospy;
import lombok.Getter;
@Getter
public class RepairService {
public boolean shouldRepair(Book book) {
return book.getTimesTaken() > 10;
}
}

View File

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

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}