I tried to user weld-junit5 with mocking a class. I mock the class because i want to know how often it will be called.
But each time i try to Mockito.verify() this mocked class it throws a "NotAMockException"
The Intellij Debugger is validating the field as: "Mock for MessageService, hashCode: XY"
I already tried to add my testclass into the WeldInitiator but it dont want to work.
"MessageService" is a real class, not an interface (a Interface wouldn't work either)
@EnableWeld
class GameServiceTest {
@WeldSetup
public WeldInitiator weld = WeldInitiator.from(GameService.class, GameCache.class,
/** Some More **/,
GameServiceTest.class).build();
@Produces
@ApplicationScoped
public MessageService createMessageServiceMock() {
return mock(MessageService.class);
}
@Inject
private MessageService messageService;
@Inject
private GameService gameService;
@Test
void handleRunningGames() {
this.gameService.handleRunningGames(null, mock(Session.class));
// This will throw a org.mockito.exceptions.misusing.NotAMockException
Mockito.verify(messageService, Mockito.times(1)).writeMessage(any(), any());
}
}
I expect, that the Injected MessageService is a real mock, on which i can call every Mockito function, but it seems not so.
Do I anything wrong, or what is the proper way to do this?
I think I just resolved this problem:
private static final MessageService messageService = mock(MessageService.class);
@Produces
@ApplicationScoped
public MessageService createMessageServiceMock() {
return messageService;
}
To give some background, the way it works is that Weld lets Mockito create the desired object and then takes that as a contextual bean instance.
However, in CDI any bean that has normal scoped needs to have a proxy that is passed around instead of that instance. So what your producer actually does (because it is
@ApplicationScoped
) is create the object, store that in the context and then also create a proxy and pass around that proxy instead. The proxy is a different object (a delegate with no state) that "knows" how to get reference to the actual instance.So what happens is that proxy gets injected into the field and you are checking the proxy object with
Mockito.verify()
call. Obviously, the proxy isn't the mock itself and so it fails. As user @second suggested, Weld offers an API to unwrap the proxy and get the contextual instance. I would argue that the API isn't "ugly", it is just a thing users shouldn't mostly care about but sometimes you cannot avoid it.You could avoid having proxy by using some of the pseudo scopes which are
@Dependent
or CDI@Singleton
. With that it should work as well and so long as it's for tests, replacing application scoped with singleton should work.As for your workaround, I do no see how that solves anything - it is basically the same producer and because of the scope it will only be invoked once, hence the static field will make no difference (as there will be a singular call to mock creation). Have you changed something else than that?