以前曾问了这个问题,但现有答案并不完全适用于我的情况。
我想测试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(verification codeviewModeltests.java:132) ->在... testsubmitcode(verification codeviewModeltests.java:133) ->在... testsubmitcode(verification codeviewmodeltests.java:134)
如果将匹配器与原始值结合在一起,则可能发生此例外: //不正确: somemethod(AnyObject()," RAW String");使用匹配项时,所有参数都必须由匹配者提供。例如: //正确的: somemethod(AnyObject(),eq("字符串by Matcher"));
有关更多信息,请参见Matchers类的Javadoc。
在 ... verification codeviewModeltests.testsubmitcode(verification codeviewmodeltests.java:135)
我不明白的是为什么它说0匹配预期?其他答案建议用any(...)
或isA(..)
替换eq(..)
。首先,我不认为这是适用的,因为错误是首先没有匹配者。其次,我已经尝试过它,并且问题仍然存在。
如果有人可以解释为什么预期有0个匹配者以及如何解决此问题,我会很感激。
update
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());
}
});
}
}
ahahaha,找到了它!您正在测试文件的第六个注释字段中错误地使用@Mock ArgumentCaptor
。
@Mock private ArgumentCaptor<AccountDataSource.VerifyMobileNumberCallback>
verifyMobileNumberCallbackCaptor;
Mockito并没有特别限制自己的基础架构,因此它并没有抓住您正在尝试嘲笑Mockito本身的事实。通过在verify
呼叫中间调用方法ArgumentCaptor.capture()
,Mockito假设您实际上正在尝试验证capture
的调用。
尽管语法巧妙,但Mockito实际上只是一台状态机,在该机器中,verify(...)
的呼叫开始验证,每个呼叫对Matcher的调用都会将匹配器描述推到内部堆栈,然后将下一个调用对Mockito Mock触发触发器确认。Mockito在参数匹配器堆栈上看到了三个匹配项,以拨打capture
的零arg调用。这就是为什么记录了3个匹配者,还有0个预期。
切换注释到@Captor
,您应该很好。