java - 我需要知道如何将方法的接口放入When() mockito 方法中
问题描述
我正在尝试在 Android Studio 中使用 mockito 模拟一个方法,但是,presenter 类中的一个方法需要一个接口作为参数,但是,当我在 Mockito 的 When() 中调用该方法时,它无法识别该接口。请参阅以下代码:
这是我的 MVP 代码示例。
SignUpContract接口:
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();
}
}
解决方案
我将您的代码复制到一个示例中以自己验证这一点。我做了一些更改,有些只是为了我的方便。请注意,我使用 JUnit5 和较新的 mockito 版本 ( 3.2.4
) 来测试它。
您不需要同时使用
@InjectMocks
和@Before
,其中任何一个都足够了。您在
doAnswer
. 它应该是args[0]
。Using
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 命名约定:类名/接口名应以大写字母开头。这通常会使代码更易于阅读。
推荐阅读
- javascript - 当我使用 jquery 时制作一个 DIV 100 %
- heroku - 我应该为生产中的小型后台作业使用 rails5 ActiveJob 默认异步适配器吗?
- java - 更新 Hibernate 中每一行的时间戳
- laravel - BadMethodCallException 方法 App\Http\Controllers\CvController::create 不存在
- c# - C# wpf 如何在折线点上绘制矩形
- c++ - 类具有相同的接口,但参数的类型不同
- mysql - 如何使用 Promise 从 node.js 中的 mysql 查询中获取结果
- javascript - 在 HTML 中制作 onclick 函数来执行 2 个功能
- android - listview 适配器中的 showContextMenuForChild - contextMenu 的样式
- java - 初学者(java中的循环语句和条件语句)