New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Mock resolver plugin #2042
Mock resolver plugin #2042
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
/* | ||
* Copyright (c) 2020 Mockito contributors | ||
* This program is made available under the terms of the MIT License. | ||
*/ | ||
package org.mockito.plugins; | ||
|
||
/** | ||
* A mock resolver offers an opportunity to resolve a mock from any instance that is | ||
* provided to the {@link org.mockito.Mockito}-DSL. This mechanism can be used by | ||
* frameworks that provide mocks that are implemented by Mockito but which are wrapped | ||
* by other instances to enhance the proxy further. | ||
*/ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This needs to be marked as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Arghs, I missed this and just hit merge. Sorry, we can still add this, though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes let's still add it. |
||
public interface MockResolver { | ||
|
||
/** | ||
* Returns the provided instance or the unwrapped mock that the provided | ||
* instance represents. This method must not return {@code null}. | ||
* @param instance The instance passed to the {@link org.mockito.Mockito}-DSL. | ||
* @return The provided instance or the unwrapped mock. | ||
*/ | ||
Object resolve(Object instance); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
/* | ||
* Copyright (c) 2020 Mockito contributors | ||
* This program is made available under the terms of the MIT License. | ||
*/ | ||
package org.mockitousage.plugins.resolver; | ||
|
||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.ExtendWith; | ||
import org.mockito.junit.jupiter.MockitoExtension; | ||
import org.mockito.junit.jupiter.MockitoSettings; | ||
import org.mockito.quality.Strictness; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.mockito.Mockito.*; | ||
|
||
@MockitoSettings(strictness = Strictness.WARN) | ||
@ExtendWith(MockitoExtension.class) | ||
class MockResolverTest { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we add a test that uses multiple resolvers? I can think of the following scenarios:
For scenario 3, should we throw maybe? What if it is order-dependent? Should we continue iterating over the resolvers until none match? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think multiple resolvers should be allowed. You can technically add several in your specified order and decompose multiple levels of nesting. It's of course an obscure scenario, but it makes sense to not forbid it since it would be extra work. I can add the tests you suggest. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, registering multiple converters in a test is not quite that easy since it would require multiple modules. I don't think its worth the noise to add this functionality just for testing such a simple corner case. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Understood. How would that work when a user wants to integrate with multiple frameworks that all expose a resolver? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They would all expose their resolver and they would be invoked in their registration order. For instance, let's say Hibernate offered a Mockito-based mocking API but returns Hibernate proxies on mocks. Those proxies are by definition never Spring Beans. But if one passed a Spring bean or a Hibernate proxy, both would traverse the resolver chain and be unpacked accordingly. I assume that normally, only one resolver would apply to any wrapped mock but since Mockito should remain agnostic to this, that would be the best approach in my eyes. |
||
|
||
@Test | ||
void mock_resolver_can_unwrap_mocked_instance() { | ||
Foo mock = mock(Foo.class), wrapper = new MockWrapper(mock); | ||
when(wrapper.doIt()).thenReturn(123); | ||
assertThat(mock.doIt()).isEqualTo(123); | ||
assertThat(wrapper.doIt()).isEqualTo(123); | ||
verify(wrapper, times(2)).doIt(); | ||
} | ||
|
||
interface Foo { | ||
int doIt(); | ||
} | ||
|
||
static class MockWrapper implements Foo { | ||
|
||
private final Foo mock; | ||
|
||
MockWrapper(Foo mock) { | ||
this.mock = mock; | ||
} | ||
|
||
Object getMock() { | ||
return mock; | ||
} | ||
|
||
@Override | ||
public int doIt() { | ||
return mock.doIt(); | ||
} | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/* | ||
* Copyright (c) 2020 Mockito contributors | ||
* This program is made available under the terms of the MIT License. | ||
*/ | ||
package org.mockitousage.plugins.resolver; | ||
|
||
import org.mockito.plugins.MockResolver; | ||
|
||
public class MyMockResolver implements MockResolver { | ||
|
||
@Override | ||
public Object resolve(Object instance) { | ||
if (instance instanceof MockResolverTest.MockWrapper) { | ||
return ((MockResolverTest.MockWrapper) instance).getMock(); | ||
} | ||
return instance; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why were the generic parameters removed from this and the other methods? I understand that they are not used, but I wonder if there are unintended side-effects of this change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just as a cleanup on the side since I am touching the code already. The generic type is erased to
Object
and has no other function, that's why I removed it. No impact on source or binary compatibility.