Skip to content

Commit

Permalink
Use single mock when backing bean is the same instance
Browse files Browse the repository at this point in the history
Fixes: quarkusio#15411
(cherry picked from commit 1c266be)
  • Loading branch information
geoand authored and gsmet committed Mar 8, 2021
1 parent 47d45c0 commit a6ad178
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package io.quarkus.it.mockbean;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;

public class SameBeanInstance {

public interface UnderTest {
void method1();
}

public interface I3 {
void method3();
}

public interface I2 {
void method2();
}

public interface I4 {
void method4();
}

@ApplicationScoped
public static class C1 implements UnderTest {
@Inject
I2 i2;

@Inject
I3 i3;

@Inject
I4 i4;

@Override
public void method1() {
i2.method2();
i3.method3();
}
}

@ApplicationScoped
public static class C2 implements I2, I3 {
@Override
public void method2() {

}

@Override
public void method3() {

}
}

@ApplicationScoped
public static class C3 implements I4 {

@Override
public void method4() {

}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package io.quarkus.it.mockbean;

import static org.mockito.Mockito.*;

import javax.inject.Inject;

import org.junit.jupiter.api.Test;

import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.junit.mockito.InjectMock;

@QuarkusTest
public class SameBeanInstanceTest {

@Inject
SameBeanInstance.UnderTest underTest;

@InjectMock
SameBeanInstance.I2 i2;

@InjectMock
SameBeanInstance.I3 i3;

@InjectMock
SameBeanInstance.I4 i4;

@Test
public void sample() {
underTest.method1();

verify(i2).method2();
verify(i3).method3();
verify(i4, never()).method4();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import javax.inject.Qualifier;

Expand All @@ -25,30 +26,45 @@ public void afterConstruct(Object testInstance) {
InjectMock injectMockAnnotation = field.getAnnotation(InjectMock.class);
if (injectMockAnnotation != null) {
Object beanInstance = getBeanInstance(testInstance, field, InjectMock.class);
Object mock = createMockAndSetTestField(testInstance, field, beanInstance);
MockitoMocksTracker.track(testInstance, mock, beanInstance);
Optional<Object> result = createMockAndSetTestField(testInstance, field, beanInstance);
if (result.isPresent()) {
MockitoMocksTracker.track(testInstance, result.get(), beanInstance);
}
}
}
current = current.getSuperclass();
}
}

private Object createMockAndSetTestField(Object testInstance, Field field, Object beanInstance) {
private Optional<Object> createMockAndSetTestField(Object testInstance, Field field, Object beanInstance) {
Class<?> beanClass = beanInstance.getClass();
// make sure we don't mock proxy classes, especially given that they don't have generics info
if (ClientProxy.class.isAssignableFrom(beanClass)) {
// and yet some of them appear to have Object as supertype, avoid them
if (beanClass.getSuperclass() != Object.class)
beanClass = beanClass.getSuperclass();
}
Object mock = Mockito.mock(beanClass);
Object mock;
boolean isNew;
Optional<Object> currentMock = MockitoMocksTracker.currentMock(testInstance, beanInstance);
if (currentMock.isPresent()) {
mock = currentMock.get();
isNew = false;
} else {
mock = Mockito.mock(beanClass);
isNew = true;
}
field.setAccessible(true);
try {
field.set(testInstance, mock);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
return mock;
if (isNew) {
return Optional.of(mock);
} else {
return Optional.empty();
}
}

static Object getBeanInstance(Object testInstance, Field field, Class<? extends Annotation> annotationType) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

Expand All @@ -27,6 +28,16 @@ static void reset(Object testInstance) {
Mockito.reset(getMocks(testInstance).stream().map(o -> o.mock).toArray());
}

static Optional<Object> currentMock(Object testInstance, Object beanInstance) {
Set<Mocked> mocks = getMocks(testInstance);
for (Mocked mocked : mocks) {
if (mocked.beanInstance == beanInstance) {
return Optional.of(mocked.mock);
}
}
return Optional.empty();
}

static class Mocked {
final Object mock;
final Object beanInstance;
Expand Down

0 comments on commit a6ad178

Please sign in to comment.