2016-09-08 6 views
1

Я изобретаю свое приложение, используя классический подход MVP. Для этого я прочитал много много статей и учебных пособий, а также то, что я пришел с в том, что лучший способ заключается в следующем:фрагмент тестирования блока Android с roboletric в приложении MPV

  • создать интерфейс для ведущего и один для представления
  • делает фрагменты и действия реализуют интерфейсы представлений
  • создают реализацию интерфейса презентатора, который принимает в конструкторе экземпляр представления, которым он управляет, и удерживает ссылку на презентатора внутри реализации представления.

Так что я создал этот класс

VIEW ИНТЕРФЕЙС

public interface SignupEmailView extends BaseView { 

     void fillEmail(String email); 

     void onEmailInvalid(String error); 

     void onDataValidated(); 
    } 

ВЕДУЩИЙ ИНТЕРФЕЙС

public interface SignupEmailPresenter { 

    void initData(Bundle bundle); 

    void validateData(String email); 
} 

VIEW РЕАЛИЗАЦИЯ

public class FrSignup_email extends BaseSignupFragmentMVP implements IBackHandler, SignupEmailView { 

     public static String PARAM_EMAIL = "param_email"; 
     @Bind(R.id.signup_step2_new_scrollview) 
     ScrollView mScrollview; 
     @Bind(R.id.signup_step2_new_lblTitle) 
     SuperLabel mLblTitle; 
     @Bind(R.id.signup_step2_new_lblSubtitle) 
     TextView mLblSubtitle; 
     @Bind(R.id.signup_step2_new_txtEmail) 
     EditText mTxtEmail; 
     @Bind(R.id.signup_step2_new_btnNext) 
     Button mBtnNext; 
     protected SignupActivityView mActivity; 
     SignupEmailPresenter mPresenter; 

     public FrSignup_email() { 
      // Required empty public constructor 
     } 

     public static FrSignup_email newInstance(String email) { 
      FrSignup_email fragment = new FrSignup_email(); 
      Bundle b = new Bundle(); 
      b.putString(PARAM_EMAIL, email); 
      fragment.setArguments(b); 
      return fragment; 
     } 

@Override 
    public void onAttach(Activity activity) { 
     super.onAttach(activity); 
     try { 
      mActivity = (SignupActivityView) activity; 
     } catch (ClassCastException e) { 
      throw new ClassCastException(activity.toString() 
        + " must implement IResetPasswordBridge"); 
     } 
    } 

     @Override 
     public View onCreateView(LayoutInflater inflater, ViewGroup container, 
           Bundle savedInstanceState) { 
      View view = loadView(inflater, container, savedInstanceState, R.layout.fragment_signup_email); 
      mPresenter = new SignupEmailPresenterImpl(this); 
      ButterKnife.bind(this, view); 
      return view; 
     } 

     @Override 
     public final void onViewCreated(View view, Bundle savedInstanceState) { 
      super.onViewCreated(view, savedInstanceState); 
      applyCircularReveal(); 

      mPresenter.initData(this.getArguments()); 

      mTxtEmail.setImeOptions(EditorInfo.IME_ACTION_NEXT); 
      mTxtEmail.setOnEditorActionListener(new TextView.OnEditorActionListener() { 
       @Override 
       public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { 
        if (actionId == EditorInfo.IME_ACTION_NEXT) { 
         mPresenter.validateData(mTxtEmail.getText().toString()); 
         return true; 
        } 
        return false; 
       } 
      }); 
      mTxtEmail.setOnTouchListener(new OnTouchCompoundDrawableListener_NEW(mTxtEmail, new OnTouchCompoundDrawableListener_NEW.OnTouchCompoundDrawable() { 
       @Override 
       public void onTouch() { 
        mTxtEmail.setText(""); 
       } 
      })); 
      mBtnNext.setOnClickListener(new View.OnClickListener() { 
       @Override 
       public void onClick(View v) { 
        mPresenter.validateData(mTxtEmail.getText().toString()); 
       } 
      }); 
     } 

     @Override 
     public void fillEmail(String email) { 
      mTxtEmail.setText(email); 
     } 

     @Override 
     public void onEmailInvalid(String error) { 
      displayError(error); 
     } 

     @Override 
     public void onDataValidated() { 
      changeFieldToValid(mTxtEmail); 
      setEmail(mTxtEmail.getText().toString()); 
      // the activity shows the next fragment 
      mActivity.onEmailValidated(); 
     } 

     @Override 
     public boolean doBack() { 
      if (!isLoading()) { 
       mActivity.onEmailBack(); 
      } 
      return true; 
     } 

     @Override 
     public void displayError(String error) { 
      changeFieldToInvalid(mTxtEmail); 
      mLblSubtitle.setText(error); 
      mLblSubtitle.setTextColor(ContextCompat.getColor(getActivity(), R.color.field_error)); 
     } 
    } 

ВЕДУЩИЙ РЕАЛИЗАЦИЯ

public class SignupEmailPresenterImpl implements SignupEmailPresenter { 
    private SignupEmailView mView; 

    public SignupEmailPresenterImpl(SignupEmailView view) { 
     mView = view; 
    } 

    @Override 
    public void initData(Bundle bundle) { 
     if (bundle != null) { 
      mView.fillEmail(bundle.getString(FrSignup_email.PARAM_EMAIL)); 
     } 
    } 

    @Override 
    public void validateData(String password) { 
     ValidationUtils_NEW.EmailStatus status = ValidationUtils_NEW.validateEmail(password); 
     if (status != ValidationUtils_NEW.EmailStatus.VALID) { 
      mView.onEmailInvalid(ValidationUtils_NEW.getEmailErrorMessage(status)); 
     } else { 
      mView.onDataValidated(); 
     } 
    } 
} 

Теперь фрагмент принадлежит деятельности, которая реализует этот вид интерфейса и имеет свой собственный ведущий

public interface SignupActivityView extends BaseView { 

    void onEmailValidated(); 
    void onPhoneNumberValidated(); 
    void onPasswordValidated(); 
    void onUnlockCodeValidated(); 
    void onResendCodeClick(); 

    void onEmailBack(); 
    void onPhoneNumberBack(); 
    void onPasswordBack(); 
    void onConfirmCodeBack(); 

    void onSignupRequestSuccess(boolean resendingCode); 
    void onSignupRequestFailed(String errorMessage); 
    void onTokenCreationFailed(); 
    void onUnlockSuccess(); 
    void onUnlockError(String errorMessage); 

    void showTermsAndConditions(); 
    void hideTermsAndConditions(); 
} 

Моя идея заключается в том, чтобы иметь модульный тест для каждого блока проекта, так для каждой реализации представления и презентатора я хочу модульный тест, поэтому я хочу, чтобы модуль тестировал мой фрагмент с помощью roboletric, и, например, я хочу проверить, что если я нажму кнопку «СЛЕДУЮЩИЙ» и правильно по электронной почте, метод onEmailValidated() называется. Это мой класс испытаний

public class SignupEmailViewTest { 

    private SignupActivity_NEW mActivity; 
    private SignupActivity_NEW mSpyActivity; 
    private FrSignup_email mFragment; 
    private FrSignup_email mSpyFragment; 
    private Context mContext; 

    @Before 
    public void setUp() { 
     final Context context = RuntimeEnvironment.application.getApplicationContext(); 
     this.mContext = context; 

     mActivity = Robolectric.buildActivity(SignupActivity_NEW.class).create().visible().get(); 
     mSpyActivity = spy(mActivity); 
     mFragment = FrSignup_email.newInstance(""); 
     mSpyFragment =spy(mFragment); 
     mSpyActivity.getFragmentManager() 
       .beginTransaction() 
       .replace(R.id.signupNew_fragmentHolder, mSpyFragment) 
       .commit(); 

     mSpyActivity.getFragmentManager().executePendingTransactions(); 
    } 

    @Test 
    public void testEmailValidation() { 
     assertTrue(mSpyActivity.findViewById(R.id.signup_step2_new_lblTitle).isShown()); 
     assertTrue(mSpyActivity.findViewById(R.id.signup_step2_new_lblSubtitle).isShown()); 

     mSpyActivity.findViewById(R.id.signup_step2_new_btnNext).performClick(); 
     assertTrue(((SuperLabel) mSpyActivity.findViewById(R.id.signup_step2_new_lblSubtitle)).getText().equals(mContext.getString(R.string.email_empty))); 

     ((EditText) mSpyActivity.findViewById(R.id.signup_step2_new_txtEmail)).setText("[email protected]"); 

     mSpyActivity.findViewById(R.id.signup_step2_new_btnNext).performClick(); 
     verify(mSpyFragment).onDataValidated(); 
     verify(mSpyActivity).onEmailValidated(); 
    } 
} 

все работает хорошо, это только последняя проверка, которая не работает. Обратите внимание, что предыдущая проверка работает, поэтому onEmailValidated вызывается точно.

Помимо этого конкретного случая, я должен обсудить несколько вопросов: Если с roboeletric я вынужден использовать операцию для создания экземпляра фрагмента, как я могу проверить фрагмент в полной изоляции (что было бы целью единичных тестов)? Я имею в виду, если я использую Robolectric.setupActivity(MyActivity.class), и действие создает экземпляр где-то фрагмента, он будет загружать активность и фрагмент, что хорошо, но что делать, если активность управляет потоком фрагментов? Как я могу проверить второй или третий фрагмент без ручной навигации? Кто-то может сказать использовать фиктивную активность и использовать FragmentTestUtil.startFragment, но что в методе onAttach() фрагмента реализован мост с родительской активностью? Является ли это не так, или эти проблемы еще не решены?

благодаря

+0

Вы отлаживали свой тест, чтобы подтвердить, что фрагмент использует объект активности шпиона? – nenick

ответ

0

На самом деле вы даже не требуют Roboelectric делать какие-либо из этих тестов.

Если каждый фрагмент/действие реализует другой интерфейс представления, вы можете реализовать поддельные представления и создавать экземпляры вместо действия/фрагмента. Таким образом, у вас могут быть изолированные тесты.

Если вы не хотите внедрять все методы интерфейса вида, вы можете использовать Mockito и заглушить только те, которые требуются вашему тестированию устройства.

Сообщите мне, если вам нужен образец кода.

+0

На самом деле я хочу протестировать уже написанную реализацию, какой смысл она делает тестирование поддельной реализации? – Apperside

+0

Используя MVP, большая часть «интересной» логики будет представлена ​​в презентаторе, и это то, что я бы испытал первым. Внедряя поддельные взгляды, вы концентрируетесь на презентаторе. Вы создаете методы stub в представлении, чтобы вернуть то, что ожидает ожидающий, проверяет логику презентатора и проверяет, что вызывается метод XXX представления. Активность/фрагмент просто содержит логику для установки текстов, фона и т. Д., Которые обычно вам не нужно проверять. – fernandospr

+1

На самом деле я хочу иметь 2 тестовых набора: тест-презентатор (который использует mocked view), где я проверяю правильность поведения (вызывается EG: методы, которые должны быть вызваны). Это тест, который я уже реализовал и где у меня нет проблем. Но мне также нужны тесты для просмотра: локальные тесты с roboeltric (EG: если метод называется некоторым текстом, установленным на определенной метке) и инструментальные тесты с эспрессо для тестирования потоков приложений и т. Д. Я сейчас сосредоточен на roboeletric, потому что быстрее, я хочу еще раз внедрить тесты эспрессо – Apperside

 Смежные вопросы

  • Нет связанных вопросов^_^