我正在尝试在Android Studio中模拟一个带有mockito的方法,但是,演示器类中的一个方法需要一个接口作为参数,但是,当我调用Mockito的When((中的方法时,它无法识别接口。请参阅以下代码:
这是我的 MVP 代码示例。
注册合约界面:
public interface SignUpContract {
interface view{
void showSignInScreen();
void onError(int code, String message);
}
interface model{
interface onFinish<R>{
void onResponse(R data);
void onError(String error);
}
void trySignIn(onFinish onFinish, String name, String password);
}
interface presenter{
void handleSignInButtonClick(String username, String password);
}
}
注册模型:
public class SignUpModel implements SignUpContract.model {
SignUpRepository repository;
public SignUpModel(){}
@Override
public void trySignIn(onFinish onFinish, String name, String password){
repository = new SignUpRepository();
String result = repository.signInUser(name, password);
if(!result.isEmpty()){
onFinish.onResponse(result);
} else {
onFinish.onError("Has error ocurred during the operation");
}
}
}
注册存储库:
public class SignUpRepository {
public String signInUser(String username, String password){
String data = "Someone";
return data;
}
}
注册演示者:
public class SignUpPresenter implements SignUpContract.presenter,
SignUpContract.model.onFinish {
SignUpContract.view view;
SignUpContract.model model;
public SignUpPresenter(SignUpContract.view view, SignUpContract.model model){
this.view = view;
this.model = model;
}
@Override
public void handleSignInButtonClick(String username, String password) {
try{
model.trySignIn(this, username, password);
}catch (Exception e){
Log.d("",e.getMessage());
}
}
@Override
public void onResponse(Object data) {
view.showSignInScreen();
}
@Override
public void onError(String error) {
view.onError(1, error);
}
}
这是我的测试:
@RunWith(MockitoJUnitRunner.class)
public class SignUpPresenterTest {
public static final String USERNAME = "username";
public static final String PASSWORD = "password";
@Mock SignUpContract.model model;
@Mock SignUpContract.model.onFinish listener;
@Mock SignUpContract.view view;
@InjectMocks SignUpPresenter SUT;
@Before
public void setUp() throws Exception {
SUT = new SignUpPresenter(view, model);
}
public void success() throws Exception {
**when(model.trySignIn(any(SignUpContract.model.onFinish.class), any(String.class), any(String.class))).then(SUT.onResponse(1));**
}
@Test
public void try_signin_success() throws Exception{
success();
ArgumentCaptor<String> ac = ArgumentCaptor.forClass(String.class);
SUT.handleSignInButtonClick(USERNAME, PASSWORD);
verify(model, times(1)).trySignIn(any(SignUpContract.model.onFinish.class), ac.capture(), ac.capture());
List<String> captures = ac.getAllValues();
Assert.assertThat(captures.get(0), is(USERNAME));
Assert.assertThat(captures.get(1), is(PASSWORD));
verify(view).showSignInScreen();
}
}
我将您的代码复制到一个示例中以自己验证这一点。 我做了一些更改,有些只是为了方便我。 请注意,我使用了带有较新的模拟版本(3.2.4
(的JUnit5来测试它。
-
您不需要同时使用
@InjectMocks
和@Before
,其中任何一个就足够了。 -
您在
doAnswer
中使用了错误的参数。应该args[0]
. -
使用
trySignIn(listener, USERNAME, PASSWORD);
对我不起作用,因为listener
指的是不同的模拟,而您实际上将被测试的类 (SUT
( 作为参数传递。
我将其更改为使用匹配器。
编辑:
我从我的示例中删除了SignUpModel
和SignUpRespository
,因为所有测试所做的只是模拟界面。该实现与您的问题并不真正相关。
listener
模拟也不需要,因为它不再使用。
import java.util.List;
import org.junit.Assert;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.stubbing.Answer;
@ExtendWith(MockitoExtension.class)
public class SignUpPresenterTest {
public interface SignUpContract {
interface view {
void showSignInScreen();
void onError(int code, String message);
}
interface model {
interface onFinish<R> {
void onResponse(R data);
void onError(String error);
}
void trySignIn(onFinish onFinish, String name, String password);
}
interface presenter {
void handleSignInButtonClick(String username, String password);
}
}
public static class SignUpPresenter implements SignUpContract.presenter, SignUpContract.model.onFinish {
SignUpContract.view view;
SignUpContract.model model;
public SignUpPresenter(SignUpContract.view view, SignUpContract.model model) {
this.view = view;
this.model = model;
}
@Override
public void handleSignInButtonClick(String username, String password) {
try {
model.trySignIn(this, username, password);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onResponse(Object data) {
view.showSignInScreen();
}
@Override
public void onError(String error) {
view.onError(1, error);
}
}
private static final String USERNAME = "username";
private static final String PASSWORD = "password";
@Mock
SignUpContract.model model;
@Mock
SignUpContract.view view;
SignUpPresenter SUT;
@BeforeEach
public void setUp() throws Exception {
SUT = new SignUpPresenter(view, model);
}
@Test
public void try_signin_success() throws Exception {
Mockito.doAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
Object[] args = invocation.getArguments();
SignUpContract.model.onFinish callback = (SignUpContract.model.onFinish) args[0];
callback.onResponse(0);
return null;
}
}).when(model).trySignIn(Mockito.any(SignUpContract.model.onFinish.class), Mockito.eq(USERNAME), Mockito.eq(PASSWORD));
ArgumentCaptor<String> ac = ArgumentCaptor.forClass(String.class);
SUT.handleSignInButtonClick(USERNAME, PASSWORD);
Mockito.verify(model, Mockito.times(1)).trySignIn(Mockito.any(SignUpContract.model.onFinish.class), ac.capture(), ac.capture());
List<String> captures = ac.getAllValues();
Assert.assertEquals(USERNAME, captures.get(0));
Assert.assertEquals(PASSWORD, captures.get(1));
Mockito.verify(view).showSignInScreen();
}
}
请注意,验证trySignIn
调用并不是真正必要的,因为如果不调用模拟上的行为定义,您的测试将不起作用。因此,这已经通过测试流程进行了验证。
还要尝试遵循 Java 命名约定:类名/接口名应以大写字母开头。这通常使代码更易于阅读。