我试图通过模拟类来使用weld-junit5。 我嘲笑该类是因为我想知道它将多久被调用一次。
但是每次我尝试Mockito.verify()这个模拟的类时,它都会引发“ NotAMockException”
Intellij调试器将字段验证为:"Mock for MessageService, hashCode: XY"
我已经尝试将我的测试类添加到WeldInitiator中,但它不想工作。
“ MessageService”是一个真实的类,而不是一个接口(一个Interface也不起作用)
@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());
}
}
我希望Injected MessageService是一个真实的模拟,我可以在其上调用每个Mockito函数,但事实并非如此。
我有什么问题吗,或者做这件事的正确方法是什么?
我想我已经解决了这个问题:
private static final MessageService messageService = mock(MessageService.class);
@Produces
@ApplicationScoped
public MessageService createMessageServiceMock() {
return messageService;
}
答案 0 :(得分:3)
为了提供一些背景知识,其工作方式是Weld让Mockito创建所需的对象,然后将其用作上下文Bean实例。
但是,在CDI中,任何具有正常作用域的bean都需要具有传递的代理而不是该实例。因此,生产者实际要做的事情(因为它是@ApplicationScoped
)是创建对象,将其存储在上下文中,然后还创建一个代理并传递该代理。代理是一个不同的对象(无状态的委托),它“知道”如何获得对实际实例的引用。
所以发生的事是代理被注入到字段中,并且您正在通过Mockito.verify()
调用来检查代理对象。显然,代理不是模拟本身,因此失败。正如用户@second所建议的那样,Weld提供了一个API来解包代理并获取上下文实例。我认为该API并不“丑陋”,这只是用户最不应该关心的事情,但有时您无法避免。
您可以通过使用一些伪作用域来避免代理,这些伪作用域为@Dependent
或CDI @Singleton
。有了它,它应该也能正常工作,并且只要用于测试,就应该用单例替换作用域内的应用程序。
关于您的解决方法,我看不到如何解决任何问题-它基本上是同一生产者,并且由于范围,它只会被调用一次,因此静态字段不会有任何区别(因为会有一个单数形式)调用以模拟制作)。除了此以外,您还有其他改变吗?
答案 1 :(得分:1)
作为我自己的JUnit 5 + Weld-JUnit用户,我正在使用以下模式。原因由Siliarus的答案解释。
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.Mock;
import ...
@EnableWeld
@ExtendWith(MockitoExtension.class) // (1)
class GameServiceTest {
@WeldSetup
public WeldInitiator weld = WeldInitiator.from(GameService.class, GameCache.class,
/** Some More **/,
GameServiceTest.class).build();
@Produces
@ApplicationScoped // (2)
@Mock // (3)
private MessageService messageServiceMock;
// @Inject // (4)
// private MessageService messageService;
@Inject
private GameService gameService;
@Test
void handleRunningGames() {
this.gameService.handleRunningGames(null, mock(Session.class));
// (5)
Mockito.verify(messageServiceMock, Mockito.times(1)).writeMessage(any(), any());
}
}
说明:
@Mock
注释由MockitoExtension
处理。同样,这只是为了方便起见,您可以在生产方法中自己创建模拟。您不需要注入模拟服务;您拥有messageServiceMock
!正如Siliarus解释的那样,这里注入的将是Weld代理。
对此注入进行更多的描述很有趣:如果bean是@ApplicationScoped
,即“正常范围”,则CDI必须注入代理。如果使用此代理而不是实际的模拟,您将获得异常。如果您遵循我在(2)中的建议,而忽略了@ApplicationScoped
,那么该bean将是依赖范围的,并且将直接注入该模拟。在这种情况下,您可以使用注入的字段,但是为什么要麻烦呢?