首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何处理org.mockito.exceptions.misusing.MissingMethodInvocationException?

如何处理org.mockito.exceptions.misusing.MissingMethodInvocationException?
EN

Stack Overflow用户
提问于 2014-10-28 21:23:15
回答 2查看 20.7K关注 0票数 4

我有一个类,看起来像

代码语言:javascript
运行
复制
@Stateless
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public class ClientRegistrationManager {
    private CrudService crudService;
    private UniqueIdGenerator uniqueIdGenerator;

    @SuppressWarnings("UnusedDeclaration")
    public ClientRegistrationManager() {
    }

    @Inject
    public ClientRegistrationManager(@Nonnull final CrudService crudService, @Nonnull final UniqueIdGenerator uniqueIdGenerator) {
        this.crudService = crudService;
        this.uniqueIdGenerator = uniqueIdGenerator;
    }

    public Member register(@Nonnull final String email, @Nonnull final String userExternalId, @Nonnull final String password) {
        final Member existingMember;
        try {
            existingMember = getMemberQueries().getMemberByEmail(email);
        } catch (final NoResultException e) {
            return createNewMemberAndGetClientDetail(email, userExternalId);
        }
        return existingMember;
    }

    ...

    @Nonnull
    protected MemberQueries getMemberQueries() {
        return new MemberQueries(crudService);
    }
}

我想通过模拟任何外部行为来测试它,所以我使用doc中描述的Pattern -1创建了getMemberQueries()

I,然后按如下方式编写我的测试

代码语言:javascript
运行
复制
public class ClientRegistrationManagerTest {

    @Mock
    private MemberQueries memberQueries;

    @Mock
    private CrudService crudService;

    @Mock
    private UniqueIdGenerator uniqueIdGenerator;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testMockedMemberQueries() {
        final ClientRegistrationManager clientRegistrationManager = new ClientRegistrationManager(crudService, uniqueIdGenerator);
        Mockito.when(clientRegistrationManager.getMemberQueries()).thenReturn(memberQueries);
        clientRegistrationManager.getMemberQueries();
        assertTrue(true);
    }
}

当我运行这段代码时,我得到的错误是

代码语言:javascript
运行
复制
org.mockito.exceptions.misusing.MissingMethodInvocationException: 
when() requires an argument which has to be 'a method call on a mock'.
For example:
    when(mock.getArticles()).thenReturn(articles);

Also, this error might show up because:
1. you stub either of: final/private/equals()/hashCode() methods.
   Those methods *cannot* be stubbed/verified.
2. inside when() you don't call method on mock but on some other object.
3. the parent of the mocked class is not public.
   It is a limitation of the mock engine.

现在这意味着我需要模拟整个ClientRegistrationManager,但是我想测试的类,我不能只是模拟整个类。

我试着在这里理解我的选择,我真的不想依赖于persistence层,那会太重了

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-10-28 23:33:12

Seelenvirtuose在评论中是正确的:根本问题是您只能在模拟和间谍上调用when,并且它通常是模拟被测类的反模式。

一种技术是使用spy,它允许您拦截和验证对真实对象的调用(“部分模拟”):

代码语言:javascript
运行
复制
final ClientRegistrationManager clientRegistrationManager =
    Mockito.spy(new ClientRegistrationManager(crudService, uniqueIdGenerator);
// doReturn is important because the call to contains a call to the mock
// before Mockito has intercepted it. In your case, this may just create a useless
// real MemberQueries, but in other cases it can throw an exception.
Mockito.doReturn(memberQueries).when(clientRegistrationManager).getMemberQueries();

作为Predrag Magic wrote,另一种方法是创建一个工厂并在测试期间替换它。如果您将工厂作为一个可选的构造函数参数传入,这一点尤其有用,因为这样生产系统就可以创建并传入它们自己的MemberQueryFactory,而您的类仍然可以按预期工作。

代码语言:javascript
运行
复制
public class ClientRegistrationManager {
  static class MemberQueriesFactory {
    MemberQueries getMemberQueries() {
      return ObjectFactory.newMemberQueries(crudService);
    }
  }

  /** Package-private. Visible for unit testing. */
  MemberQueriesFactory memberQueriesFactory = new MembersQueryFactory();
}

@RunWith(MockitoJUnitRunner.class)
public class ClientRegistrationManagerTest {
  @Mock ClientRegistrationManager.MembersQueryFactory mockMembersQueryFactory;

  private ClientRegistrationManager getManagerForTest() {
    ClientRegistrationManager manager = new ClientRegistrationManager();
    manager.memberQueriesFactory = mockMembersQueryFactory;
    return manager;
  }
}

不过,我最喜欢的方式是跳过Mockito,直接覆盖测试中的待测类:

代码语言:javascript
运行
复制
@RunWith(MockitoJUnitRunner.class)
public class ClientRegistrationManagerTest {
  @Mock MemberQueries memberQueries;

  private ClientRegistrationManager getManagerForTest() {
    ClientRegistrationManager manager = new ClientRegistrationManager() {
      @Override void getMemberQueries() {
        return memberQueries;
      }
    };
  }
}
票数 3
EN

Stack Overflow用户

发布于 2014-10-28 21:33:41

一种选择是使用某种对象工厂在测试类中创建新对象,然后模拟该工厂。

代码语言:javascript
运行
复制
protected MemberQueries getMemberQueries() {
    return ObjectFactory.newMemberQueries(crudService);
}

如果出于某种原因,您只想模拟某些对象的选定方法,而不是整个对象,您可以使用Mockito来实现。查看this示例。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/26609499

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档