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

时间:2014-10-28 13:23:15

标签: java unit-testing mocking mockito

我有一个类似于

的课程
@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()

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

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

当我运行时,我收到错误

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图层,这将太重了

2 个答案:

答案 0 :(得分:3)

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

一种技巧是使用spy,它可以拦截和验证对真实对象的调用("部分模拟"):

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,你的类仍然可以按预期工作。

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并直接覆盖测试中的测试类:

@RunWith(MockitoJUnitRunner.class)
public class ClientRegistrationManagerTest {
  @Mock MemberQueries memberQueries;

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

答案 1 :(得分:0)

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

protected MemberQueries getMemberQueries() {
    return ObjectFactory.newMemberQueries(crudService);
}

如果由于某种原因,你想要模拟一些对象的选定方法而不是整个对象,你可以用Mockito来做。查看this示例。