Skip to content

Commit

Permalink
Merge pull request #360 from yrodiere/mockito-classloader
Browse files Browse the repository at this point in the history
Always use the classloader of mocked classes when creating/configuring mocks
  • Loading branch information
gsmet authored Sep 8, 2022
2 parents 55b3bfc + d883273 commit 4ddf48f
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,10 @@ public final class GitHubMockContextImpl implements GitHubMockContext, GitHubMoc

GitHubMockContextImpl(Answers defaultAnswers) {
this.defaultAnswers = defaultAnswers;
fileDownloader = Mockito.mock(GitHubFileDownloader.class);
service = Mockito.mock(GitHubService.class);
fileDownloader = MockitoUtils.doWithMockedClassClassLoader(GitHubFileDownloader.class,
() -> Mockito.mock(GitHubFileDownloader.class));
service = MockitoUtils.doWithMockedClassClassLoader(GitHubFileDownloader.class,
() -> Mockito.mock(GitHubService.class));
repositories = new MockMap<>(GHRepository.class);
clients = new MockMap<>(GitHub.class,
// Configure the client mocks to be offline, because we don't want to send HTTP requests.
Expand Down Expand Up @@ -216,15 +218,16 @@ private DefaultableMocking<T> getOrCreate(ID id) {
}

private DefaultableMocking<T> getOrCreate(ID id, Consumer<T> consumerIfCreated) {
return map.computeIfAbsent(id, theId -> {
return map.computeIfAbsent(id, theId -> MockitoUtils.doWithMockedClassClassLoader(clazz, () -> {
DefaultableMocking<T> result = create(theId);
consumerIfCreated.accept(result.mock());
return result;
});
}));
}

private DefaultableMocking<T> create(Object id) {
return DefaultableMocking.create(clazz, id, mockSettingsContributor);
return MockitoUtils.doWithMockedClassClassLoader(clazz,
() -> DefaultableMocking.create(clazz, id, mockSettingsContributor));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.quarkiverse.githubapp.testing.internal;

import java.util.function.Supplier;

public class MockitoUtils {

private MockitoUtils() {
}

public static <T> T doWithMockedClassClassLoader(Class<?> mockedClass, Supplier<T> action) {
ClassLoader mockedClassClassLoader = mockedClass.getClassLoader();
ClassLoader old = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(mockedClassClassLoader);
return action.get();
} finally {
Thread.currentThread().setContextClassLoader(old);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,13 @@ public final class DefaultableMocking<M> {
private static final MockMaker mockMaker = Plugins.getMockMaker();

public static <M> DefaultableMocking<M> create(Class<M> clazz, Object id, Consumer<MockSettings> mockSettingsContributor) {
ClassLoader old = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(clazz.getClassLoader());
StubDetectingInvocationListener listener = new StubDetectingInvocationListener();
MockSettings mockSettings = Mockito.withSettings().name(clazz.getSimpleName() + "#" + id)
.withoutAnnotations()
.invocationListeners(listener);
mockSettingsContributor.accept(mockSettings);
M mock = Mockito.mock(clazz, mockSettings);
return new DefaultableMocking<>(mock, listener);
} finally {
Thread.currentThread().setContextClassLoader(old);
}
StubDetectingInvocationListener listener = new StubDetectingInvocationListener();
MockSettings mockSettings = Mockito.withSettings().name(clazz.getSimpleName() + "#" + id)
.withoutAnnotations()
.invocationListeners(listener);
mockSettingsContributor.accept(mockSettings);
M mock = Mockito.mock(clazz, mockSettings);
return new DefaultableMocking<>(mock, listener);
}

private final M mock;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

import io.quarkiverse.githubapp.testing.internal.MockitoUtils;

/**
* The default answer for all {@link GHEventPayload} spies.
* <p>
Expand Down Expand Up @@ -41,11 +43,13 @@ public Object answer(InvocationOnMock invocation) throws Throwable {
}
GHObject castOriginal = (GHObject) original;
Class<? extends GHObject> type = castOriginal.getClass();
DefaultableMocking<? extends GHObject> mocking = defaultableMockingProvider.apply(castOriginal);
return Mockito.mock(type, withSettings().stubOnly()
.withoutAnnotations()
.spiedInstance(original)
.defaultAnswer(new GHObjectSpyDefaultAnswer(clientSpy, this, mocking)));
return MockitoUtils.doWithMockedClassClassLoader(type, () -> {
DefaultableMocking<? extends GHObject> mocking = defaultableMockingProvider.apply(castOriginal);
return Mockito.mock(type, withSettings().stubOnly()
.withoutAnnotations()
.spiedInstance(original)
.defaultAnswer(new GHObjectSpyDefaultAnswer(clientSpy, this, mocking)));
});
}

}

0 comments on commit 4ddf48f

Please sign in to comment.