Мне нужно знать, как поместить интерфейс метода внутри метода mockito When () - PullRequest
0 голосов
/ 29 января 2020

Я пытаюсь смоделировать метод с помощью mockito в Android Studio, но одному из методов в классе Presenter нужен интерфейс в качестве параметра, но, когда я вызываю метод внутри When () Mockito, это не распознать интерфейс. См. Код ниже:

Это мой пример кода 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);
}
}

SignUpModel:

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");
    }

}
}

SignUpRepository:

public class SignUpRepository {

public String signInUser(String username, String password){
    String data = "Someone";
    return data;
}

}

SignUpPresenter:

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();

}

}

1 Ответ

1 голос
/ 30 января 2020

Я скопировал ваш код в пример, чтобы проверить это сам. Я сделал несколько изменений, некоторые только для моего удобства. Обратите внимание, что для тестирования я использовал JUnit5 с более новой версией mockito (3.2.4).

  1. Вам не нужно использовать оба параметра @InjectMocks и @Before, ни один из них достаточно.

  2. Вы используете неверный аргумент внутри doAnswer. Это должно было быть args[0].

  3. Использование 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 Соглашениям по именованию: имена классов / интерфейсов должны начинаться с заглавной буквы. Обычно это облегчает чтение кода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...