【问题标题】:Mockito does not recognize classMockito 不识别类
【发布时间】:2019-08-10 15:26:13
【问题描述】:

我尝试使用weld-junit5 模拟一个类。 我嘲笑这门课,因为我想知道它多久会被调用一次。

但每次我尝试 Mockito.verify() 这个模拟类时,它都会抛出“NotAMockException”

Intellij 调试器正在验证该字段为:"Mock for MessageService, hashCode: XY"

我已经尝试将我的测试类添加到 WeldInitiator 中,但它不想工作。

“MessageService”是一个真正的类,而不是一个接口(接口也不起作用)

Docs

@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;
  }

【问题讨论】:

  • 问题是weld 将东西包装到代理对象中。您可以使用answer 中提到的解决方案,但这很丑。
  • 我猜你最好不要在单元测试中使用CDI/weld来测试你的类,而是使用mockito@Mock@InjectMocks注解注入依赖项.
  • 谢谢@second 我发现这似乎不是进行测试的最佳方式,但目前还可以。我添加了我的解决方案。
  • 请阅读stackoverflow.com/help/tagging 并记住它以备将来的问题(我删除了前导标签)

标签: java mockito cdi weld-junit5


【解决方案1】:

为了提供一些背景知识,它的工作方式是 Weld 让 Mockito 创建所需的对象,然后将其作为上下文 bean 实例。

但是,在 CDI 中,任何具有正常范围的 bean 都需要有一个可以传递的代理而不是该实例。所以你的生产者实际上做的(因为它是@ApplicationScoped)是创建对象,将它存储在上下文中,然后创建一个代理并传递该代理。代理是一个不同的对象(没有状态的委托),它“知道”如何获取对实际实例的引用。

所以发生的事情是代理被注入到字段中,并且您正在使用Mockito.verify() 调用检查代理对象。显然,代理不是模拟本身,因此它失败了。正如用户@second 所建议的,Weld 提供了一个 API 来解包代理并获取上下文实例。我认为 API 并不“丑陋”,它只是用户不应该最关心的事情,但有时你无法避免它。

您可以通过使用 @Dependent 或 CDI @Singleton 的一些伪作用域来避免使用代理。有了它,它应该也能正常工作,只要它用于测试,用单例替换范围内的应用程序应该可以工作。

至于您的解决方法,我看不出它如何解决任何问题 - 它基本上是相同的生产者,并且由于范围的原因,它只会被调用一次,因此静态字段不会有任何区别(因为会有一个单数调用模拟创建)。你有没有改变其他的东西?

【讨论】:

  • 感谢您的回答。我尝试使用@Singleton,但这会引发以下错误,因为服务是@StatelessPseudo scoped bean has circular dependencies. Dependency path
【解决方案2】:

作为 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());
    }
}

解释:

  1. 为了方便,我使用了 Mockito 扩展
  2. 您实际上不需要给它应用范围
  3. @Mock 注释由MockitoExtension 处理。同样,这只是为了方便,您可以在生产者方法中自己创建模拟。
  4. 您不需要注入模拟服务;你有messageServiceMock!正如 Siliarus 所解释的那样,这里注入的东西将是 Weld 代理。

    多描述一下这个注入很有趣:如果 bean 是@ApplicationScoped,即“正常范围”,CDI 必须注入一个代理。如果你使用这个代理而不是实际的模拟,你会得到异常。如果您遵循我在 (2) 中的建议并省略 @ApplicationScoped,则 bean 将是依赖范围的,并且直接注入模拟。在这种情况下,您可以使用注入的字段,但何必呢?

  5. 直接使用模拟。

【讨论】:

  • 感谢您的回答,但不幸的是,这对我来说是有效的。如果我删除@ApplicationScope,我会收到以下问题Pseudo scoped bean has circular dependencies. Dependency path,这似乎是正确的(服务是Stateless。添加@ApplicationScoped 时我得到Cannot return null from a non-dependent producer method(我知道为什么).. 如果不使用@Mock 和直接声明它告诉我没有调用任何方法。
  • 嗯,“无法返回 null”错误可能是因为 Weld JUnit 扩展在 Mockito 扩展之前执行。您可能会通过以下方式获得好运:(a) 将 EnableWeld after @ExtendWith(MockitoExtension.class) (b) 使用 @ExtendWith({MockitoExtension.class, WeldJunit5Extension.class})。我使用的是@EnableAutoWeld,之前是@ExtendWith(Mockito)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-10
  • 1970-01-01
  • 2017-04-29
  • 1970-01-01
  • 2017-06-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多