首页 > 解决方案 > 我需要知道如何将方法的接口放入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();

}

}

标签: javaandroidmockito

解决方案


我将您的代码复制到一个示例中以自己验证这一点。我做了一些更改,有些只是为了我的方便。请注意,我使用 JUnit5 和较新的 mockito 版本 ( 3.2.4) 来测试它。

  1. 您不需要同时使用@InjectMocks@Before,其中任何一个都足够了。

  2. 您在doAnswer. 它应该是args[0]

  3. UsingtrySignIn(listener, USERNAME, PASSWORD);对我不起作用,因为listener当您实际通过被测类 ( SUT) 作为参数时,它指的是不同的模拟。

    我将其更改为使用匹配器。


编辑:
我从我的示例中删除SignUpModelSignUpRespository因为所有测试都是模拟界面。该实施与您的问题并不真正相关。

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 命名约定:类名/接口名应以大写字母开头。这通常会使代码更易于阅读。


推荐阅读