【问题标题】:"Mockito 0 matchers expected, 3 recorded" - Why are 0 matchers expected?“Mockito 0 匹配器预期,3 记录” - 为什么预期 0 匹配器?
【发布时间】:2017-10-04 19:08:39
【问题描述】:

以前有人问过这个问题,但现有答案并不完全适用于我的情况。

我想测试submitCode() 方法:

public class VerificationCodeViewModel{

    //Input
    public final ObservableField<String> verificationCode           = new ObservableField<>();

    //Output
    public final ObservableField<String> requestError               = new ObservableField<>();
    public final ObservableBoolean loading                          = new ObservableBoolean();
    public final ObservableField<LoginCredentials> loginCredentials = new ObservableField<>();

    @NonNull private final Context context;
    @NonNull private final UnverifiedUser unverifiedUser;
    @NonNull private final CampaignRepository campaignRepository;
    @NonNull private final AccountRepository accountRepository;
    @NonNull private final VerificationCodeNavigator navigator;

    public VerificationCodeViewModel(@NonNull Context context,
                                     @NonNull UnverifiedUser unverifiedUser,
                                     @NonNull CampaignRepository campaignRepository,
                                     @NonNull AccountRepository accountRepository,
                                     @NonNull VerificationCodeNavigator navigator) {

        this.context = context;
        this.unverifiedUser = unverifiedUser;

        this.campaignRepository = campaignRepository;
        this.accountRepository = accountRepository;
        this.navigator = navigator;
    }

    public void submitCode() {

        loading.set(true);

        String sourceCampaign = null;
        if (campaignRepository.getCampaign() != null) {
            sourceCampaign = campaignRepository.getCampaign().getSource();
        }

        this.accountRepository.verifyMobileNumber(
                this.unverifiedUser,
                this.verificationCode.get(),
                sourceCampaign,
                new AccountDataSource.VerifyMobileNumberCallback() {
                    @Override
                    public void onVerificationSuccess(UnverifiedUser.Entity entity) {
                        loading.set(false);
                        loginCredentials.set(createLoginCredentials());
                        navigator.finishActivity(true);
                    }

                    @Override
                    public void onVerificationFailure(@Nullable String message) {
                        loading.set(false);
                        requestError.set(message);
                    }
                }
        );
    }
}

我有以下测试用例:

public class VerificationCodeViewModelTests {

    private VerificationCodeViewModel viewModel;

    @Mock private Context context;

    @Mock private UnverifiedUser unverifiedUser;

    @Mock private CampaignRepository campaignRepository;

    @Mock private AccountRepository accountRepository;

    @Mock private VerificationCodeNavigator navigator;

    @Mock private ArgumentCaptor<AccountDataSource.VerifyMobileNumberCallback> verifyMobileNumberCallbackCaptor;


    @Before
    public void setupVerificationCodeViewModel(){

        MockitoAnnotations.initMocks(this);

        viewModel = new VerificationCodeViewModel(
                context,
                unverifiedUser,
                campaignRepository,
                accountRepository,
                mock(VerifyMobileNumberActivity.class)//navigator
        );
    }

    @Test
    public void testSubmitCode(){

        viewModel.verificationCode.set(VERIFICATION_CODE);
        viewModel.submitCode();

        assertTrue(viewModel.loading.get());

        verify(accountRepository).verifyMobileNumber(
                eq(unverifiedUser),//line 132
                eq(VERIFICATION_CODE),//line 133
                eq(CAMPAIGN_SOURCE),//line 134
                verifyMobileNumberCallbackCaptor.capture());//line 135

        UnverifiedUser.Entity entity = mock(UnverifiedUser.Entity.class);
        when(entity.getId()).thenReturn(ENTITY_ID);

        verifyMobileNumberCallbackCaptor.getValue().onVerificationSuccess(entity);

        assertFalse(viewModel.loading.get());
        assertEquals(viewModel.loginCredentials.get().getUsername(),UNVERIFIED_USER_EMAIL);
        assertEquals(viewModel.loginCredentials.get().getPassword(),UNVERIFIED_USER_PASSWORD);

        verify(navigator).finishActivity(true);
    }
}

当我验证 accountRepository.verifyMobileNumber 被调用时,我收到以下错误:

org.mockito.exceptions.misusing.InvalidUseOfMatchersException: 参数匹配器的使用无效!预期 0 个匹配器,记录 3 个: -> 在 ...testSubmitCode(VerificationCodeViewModelTests.java:132) -> 在 ...testSubmitCode(VerificationCodeViewModelTests.java:133) -> 在 ...testSubmitCode(VerificationCodeViewModelTests.java:134)

如果匹配器与原始值组合,则可能会发生此异常: //不正确: someMethod(anyObject(), "原始字符串");使用匹配器时,所有参数都必须由匹配器提供。例如: //正确的: someMethod(anyObject(), eq("String by matcher"));

有关更多信息,请参阅 Matchers 类的 javadoc。

在 ...VerificationCodeViewModelTests.testSubmitCode(VerificationCodeViewModelTests.java:135)

我不明白的是为什么它说 0 匹配预期?其他答案建议用any(...)isA(..) 替换eq(..)。 首先,我不认为这是适用的,因为错误是一开始就没有匹配器; 其次,我试过了,问题仍然存在。

如果有人能解释为什么需要 0 个匹配器以及如何解决这个问题,我将不胜感激。

更新

AccountRepository.verifyMobileNumber()的实现是:

AccountRepository.java

public class AccountRepository implements AccountDataSource {
    @Override
    public void verifyMobileNumber(@NonNull UnverifiedUser unverifiedUser, 
                                   @NonNull String verificationCode, 
                                   @Nullable String sourceCampaign, 
                                   @NonNull VerifyMobileNumberCallback callback) {
        this.remoteSource.verifyMobileNumber(unverifiedUser, verificationCode, sourceCampaign, callback);
    }
}

AccountRemoteDataSource.java

public class AccountRemoteDataSource implements AccountDataSource {

    @Override
    public void verifyMobileNumber(@NonNull UnverifiedUser unverifiedUser,
                                   @NonNull String verificationCode,
                                   @Nullable String sourceCampaign,
                                   @NonNull final VerifyMobileNumberCallback callback) {

        accountService().verifyMobileNumber(/*params*/).enqueue(new Callback() {
            @Override
            public void onResponse(Response response, Retrofit retrofit) {
                try{
                    //parse response
                    callback.onVerificationSuccess(entity);
                } catch (Exception e) {
                    callback.onVerificationFailure(e.getMessage());
                }
            }

            @Override
            public void onFailure(Throwable t) {
                callback.onVerificationFailure(t.getMessage());
            }
        });
    }
}

【问题讨论】:

  • 您确定错误在该行吗?请发布整个错误
  • @JuanCruzSoler 我已经包含了完整的错误消息并在代码 sn-p 中标记了相关行。
  • 感谢您迄今为止提供的完整且标记良好的源代码,但另外,请发布verifyMobileNumber 的实现。很有可能是final; AccountRepository 和verifyMobileNumber 都应该是非final。 (我有点惊讶它说“记录了 3 个匹配器”而不是“记录了 4 个匹配器”,但预期为 0 是一个相当不错的信号,表明您没有验证您的想法。)
  • @JeffBowman 谢谢!我已经包含了实现。 AccountRepositoryviewModel 中的 final 字段(删除 final 并没有解决问题)。 AccountRepository,remoteSourceverifyMobileNumber() 都不是final

标签: android mockito matcher


【解决方案1】:

哈哈哈,找到了!您在测试文件的第六个带注释字段中错误地使用了 @Mock ArgumentCaptor

@Mock private ArgumentCaptor<AccountDataSource.VerifyMobileNumberCallback>
    verifyMobileNumberCallbackCaptor;

Mockito 没有对自己的基础架构进行特殊处理,因此它没有发现您试图模拟 Mockito 本身的事实。通过在verify 调用中间调用ArgumentCaptor.capture() 方法,Mockito 假定您实际上是在尝试验证对capture 的调用。

尽管语法很巧妙,但 Mockito 实际上只是一个状态机,其中对 verify(...) 的调用会启动验证,对匹配器的每次调用都会推送匹配器描述 onto an internal stack,然后对 Mockito 模拟触发器的下一次调用验证。 Mockito 在参数匹配器堆栈上看到三个匹配器,用于对capture 的零参数调用。这就是为什么记录了 3 个匹配器,而预期为 0 个。

将该注释切换为@Captor,您应该一切顺利。

【讨论】:

  • 捂脸!当然,现在一切都说得通了。非常感谢您花时间发现这一点
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-23
  • 1970-01-01
  • 2014-12-12
  • 1970-01-01
  • 2011-01-18
  • 2011-09-26
相关资源
最近更新 更多