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
mockito3.4.0 InlineByteBuddyMockMaker can't work with spring cglib proxy, but ByteBuddyMockMaker works #1980
Comments
In this case, the mock is not registered correctly. Mockito has no knowledge of Spring and could not do much about this. Have you adressed this issue with Spring? How do you get hold of this proxied instance? |
@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
@AutoConfigureMockMvc
public class AppGroupClusterActionTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private AppGroupClusterService appGroupClusterService;
@SpyBean
private AppGroupClusterAction appGroupClusterAction;
@Captor
private ArgumentCaptor<Object[]> argumentCaptor;
@Test
public void testUpdateExecuteAuditSpel() throws Exception {
ServiceResult mockRes = new ServiceResult();
mockRes.setSuccess(true);
doReturn(mockRes).when(appGroupClusterService).update(
Mockito.any(), Mockito.any(), Mockito.any(),
Mockito.any(), Mockito.any(), Mockito.any(),
Mockito.any(), Mockito.any(), Mockito.any());
mockMvc.perform(
MockMvcRequestBuilders.post("/platforms/{platform}/appGroupNames/{appGroupName}/clusterNames/{clusterName}/update",
"platform", "appGroupName", "clusterName").content("{}"))
.andExpect(status().isOk())
.andReturn();
// not work
// verify(appGroupClusterAction).addAfterAuditResult(argumentCaptor.capture());
verify((AppGroupClusterAction) AopTestUtils.getTargetObject(appGroupClusterAction)).addAfterAuditResult(argumentCaptor.capture());
assertArrayEquals(argumentCaptor.getValue(), new Object[]{"appGroupName", "platform", "clusterName", "{}"});
verify((AppGroupClusterAction) AopTestUtils.getTargetObject(appGroupClusterAction)).addAfterAuditResult(argumentCaptor.capture());
assertArrayEquals(argumentCaptor.getValue(), new Object[]{"appGroupName", "platform", "clusterName", "{}"});
}
} this is the code. I haven't addressed this issue with Spring, because it works before mockito3.4.0, I don't know whether the mockito need to be compatible with before? |
I'd say this issue would need to be adressed in Spring. I assume that the |
I've taken a look at this from the Spring Boot side of things. The TL;DR is that Spring's creating a CGLib proxy of the spy that Mockito has created. This proxied spy is passed into
I should also note that, in my testing, this isn't the case. The problem exists with Mockito 3.4, 3.3, and 3.1 with the difference in behaviour depending on whether or not you're using mockito-inline. |
@raphw @mockitoguy can you help? |
Thanks for filing the issue. Could you please provide us a minimal reproduction case that we can clone/download to debug the issue? Thanks in advance! |
Sorry, I missed this. The reason this works with the The reason it does not work with the From Mockito's side things are simple: if you do not provide "our" mock objects to us, we do not guarantee to work, not in the past or in the future. That it does currently work with the subclass mock maker facility is rather accidental and might change in the future. If Spring cannot do anything about this, you'd need to unproxy the objects explicitly. |
As things stand, I don't believe we can as there's no opportunity for Spring to remove its proxy when the proxied spy is passed into I've wondered in the past if Mockito could offer a low-level API that provides a mechanism for pre-processing the instance that's passed in, but concluded that it wouldn't be broadly usable. I'd be delighted if someone who's more familiar with Mockito's internals believes it could be done. In the absence of some inspiration on the Mockito side, our current plan is to make it easier for users to work with both the Spring proxy and the "bare" Mockito spy. |
I could certainly add a mechanism for this, it would not create too much overhead and we already have a plugin mechanism in place for it. We would just add a non-operational mechanism by default but allow frameworks to unwrap "their" mocks. You could declare this by Mockito's default plugin mechanism. I will look into adding something like this to Mockito. |
I just added an API. Could you try the mock-resolver-plugin branch where Spring would need to implement the This way, I think this problem can be solved and Spring stops relying on the implementation detail. |
Thanks, @raphw. I think this moves things in the right direction and allows almost all of our Mockito-related tests to pass with the inline mock maker. I am, however, still seeing a failure when calling public static <T> void resetMock(T mock) {
mock = resolve(mock);
MockHandler oldHandler = getMockHandler(mock);
MockCreationSettings settings = oldHandler.getMockSettings();
MockHandler newHandler = createMockHandler(settings);
mockMaker.resetMock(mock, newHandler, settings);
} |
Good catch! I'll fix that up! |
I updated the branch and doublechecked that I did not miss anything else. Let me know if this works for you! |
@wilkinsona Did you have a chance to validate my latest revision of this? |
Not yet, sorry. I'll try and take another look at it this week. |
It took a little longer than I'd hoped, but I've just tried the updated branch and all of Spring Boot's Mockito-related tests now pass with the inline mock maker. Thank you. |
@raphw @wilkinsona Can someone tell me how I can leverage or otherwise take advantage of whatever fix was made in #2042 and get my @SpyBeans to work correctly with Mockito of any version? I've tried changing my version from v3.4.0 to v3.6.0 (where the change was ultimately merged) but that alone has not improved the situation. Thanks in advance! |
I upgrade mockito to 3.4.0, and the mockMaker is InlineByteBuddyMockMaker, I use SpyBean to spy a spring bean, and then verify the bean, it throws NotAMockException.
then i debug, find that the mocks.get(mock) can't find it because the mock is CGLIB proxy, not the original type.
when I use the verify((AppGroupClusterAction) AopTestUtils.getTargetObject(appGroupClusterAction)) to verify. it works.
but the before mockMaker ByteBuddyMockMaker works. so can the InlineByteBuddyMockMaker be compatible with the before?
The text was updated successfully, but these errors were encountered: