Android Testing Part 2 — Mais MVP

Dario Mungoi
5 min readOct 6, 2016

--

No último post dei uma breve introdução sobre a importância dos testes em aplicações android e os dois tipos de testes que podemos efectuar em uma app nomeadamente Unit Tests e Instrumental Tests.
Falei ainda da importância e do objectivo desta série em criar de ambientes favoráveis para testes antes mesmo de começar a escrever código.

Ainda no post anterior fiz a introdução ao padrão de arquitectura MVP que de forma resumida pressupõe que a aplicação esta dividida em 3 componentes:

  • View: Responsável pela apresentação da informação ao utilizador e todo outro tipo de operações relacionadas a interface gráfica.
  • Model: Responsável pela representação de dados e mecanismos de escrita/leitura de dados a um determinado repositório(BD Local, Servidor).
  • Presenter: Responsável por preparar os dados antes de serem apresentados pelo utilizador ou enviados para o repositório.
    O presenter encontra-se no meio como o único mecanismo de comunicação entre o View e o Model.

Como aplicar o MVP em uma app?

Aplicar o MVP em uma app não tem nenhum segredo e temos de olhar para o projecto pelos seguintes pontos:

  1. Activities e Fragments — Irão representar a componente View da aplicação e serão responsáveis por realizar operações de UI. (Exemplo: Mostrar elementos, registar listeners nos botões, Iniciar novas Activities etc).
  2. Definir as operações que podemos realizar no presenter após receber um evento da View, criar uma interface e implementar os metodos em uma classe.
    Esta interface será utilizada para efectuar a comunicação do view para o presenter.
  3. Definir como os dados estarão representados e o mecanismos para a busca dos mesmos.

Tomando em consideração uma app com as simples funcionalidades de visualizar uma lista de eventos e posteriormente os detalhes do evento, aplicaríamos o padrão MVP da seguinte forma:

Organização de pacotes por funcionalidades

O primeiro passo consiste na mudança de como organizamos os pacotes e as nossas classes no projecto.
Para quem esteja a começar o desenvolvimento de android, normalmente não tem nenhuma organização estrita para os pacotes ou usa simplesmente a forma mais utilizada que consiste em organizar as classes em pacotes com componentes similares(Exemplo: Guardar todos os Fragment em um pacote de fragments ou Adapters em um pacote só com adapters).

Para facilmente aplicar o padrão MVP, recomendo que passemos a organizar os pacotes por funcionalidades da aplicação . (Exemplo: Todas as classes responsáveis pela funcionalidade de listar eventos no mesmo pacote)

com.app.eventos.blog → raiz do pacote
eventos → pacote com classes para listar eventos
evento → pacote com classes para visualização dos detalhes de um evento
login → pacote com as classes responsáveis pelo Login

Tendo os pacotes devidamente organizados, tomemos como exemplo para aplicar o MVP a funcionalidade de listar eventos seguindo os passos abaixo.

Criar um Contracto

Assumindo que temos uma ideia de como será a tela para listar eventos e sabemos como a UI irá se comportar e interagir com o utilizador, vamos primeiro criar uma interface que irá conter dentro duas interfaces denominadas View e UserActionsListener que serão responsáveis por definir os métodos que representam as operações a serem efectuadas pela View e pelo Presenter respectivamente.

public interface EventsContract {

interface View{
void showEvents(List<Event> events);
void showEmptyScreen(boolean isDataSetEmpty);
void showErrorScreen(boolean operationHasFailed);
void showProgressBar(boolean isLoading);
void showToast(@StringRes int messageResid);
void openEventDetailsUi(Event event);
}

interface UserActionsListener{
void loadEvent(EventsQueryForm queryForm);
void selectEvent(Event event);
}
}

Como mostra o código acima, a interface View define métodos que serão implementados pela View para realizar as diferentes operações como apresentar a lista de eventos, iniciar uma nova Activity com os detalhes do evento etc.
Por sua vez, a Interface UserActionsListener define os métodos a serem implementados pelo presenter que representam operações que originam da interacção do utilizador com a UI.

Criar o Presenter

Criar a classe que irá comportar-se como Presenter consiste em simplesmente criar uma classe que implementa a interface UserActionListener. Naturalmente ao implementar esta interface, todos os métodos definidos nela deverão ser obrigatoriamente implementados.

Como mostra a figura com a representação do padrão MVP no post anterior, o view e o presenter não se comunicam de forma directa mas sim através das interfaces definidas no contracto.
Sendo assim, temos de criar uma instância global no presenter com a interface da view que terá o seu valor atribuído passando a interface da view como parâmetro do construtor do presenter.

public class EventsPresenter implements EventsContract.UserActionsListener {

private EventsContract.View mView;

public EventsPresenter(EventsContract.View view, EventsRepoistory eventsRepoistory){
this.mView = view;
}


@Override
public void loadEvent(EventsQueryForm queryForm) {
mView.showProgressBar(true);
}

@Override
public void selectEvent(Event event) {

}
}

Criar a View

Como tinha mencionado acima, a escolha do MVP como arquitectura padrão foi baseada no objectivo de criar separação de tarefas entre os diferentes componentes de uma aplicação android e garantir que componentes como Fragments e Activities não ficassem cheios de código e lógica desnecessária e focassem-se apenas em realizar operações relacionadas a UI.

Para o nosso exemplo, a nossa View será um Fragment que simplesmente irá implementar a interface View e os seus métodos.

Estes métodos serão invocados pelo presenter para que a devida operação seja efectuada.

Se prestaram atenção ao último pedaço de código o presenter não tem nenhuma informação sobre como é efectuada a operação de mostrar ou não o ProgressBar assim como a View não tem noção de como e efectuado o loading dos eventos.

O código abaixo mostra o fragment que será responsável por apresentar a lista dos eventos . Este fragment implmenta a interface View e cria uma instância do UserActionListener onde passa a interface View como parâmetro do construtor.

public class EventsListFragment extends Fragment implements  EventsContract.View {

private ProgressBar mProgressBar;
private ViewStub mErrorScreenStub;
private RecyclerView mRecyclerView;
private EventsListAdapter mAdapter;
private FragmentEventsListBinding mRootBinding;
private EventsContract.UserActionsListener mUsersActionListener;

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mUsersActionListener = new EventsPresenter(this, Injection.provideEventsRepository());
mAdapter = new EventsListAdapter(mUsersActionListener);
}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
///Other code here
return mRootBinding.getRoot();
}


@Override
public void showEvents(List<Event> events) {

}

@Override
public void showEmptyScreen(boolean isDataSetEmpty) {

}
@Override
public void showErrorScreen(boolean operationHasFailed) {

}

@Override
public void showToast(@StringRes int messageResid) {
String message = getResources().getString(messageResid);
Toast.makeText(getContext(),message,Toast.LENGTH_SHORT).show();;
}

@Override
public void openEventDetailsUi(Event event) {

}

@Override
public void showProgressBar(boolean isLoading) {
mProgessBar.setVisibility(View.VISIBLE);
}


}

Tendo a View e o Presenter prontos, o próximo passo seria a criação das classes que vão representar a componente Model do MVP.
Infelizmente não implementaremos o Model neste post pois acredito que faz mais sentido falar e mostrar a implementação no próximo ao introduzir o conceito de Dependency Injection.

Resumindo o post de hoje, com a implementação do MVP para o nosso exemplo da lista dos eventos podemos notar que:

  • O Fragment responsável por apresentar a lista dos eventos apenas saberá realizar todas as operações relacionadas a UI.
  • O presenter irá responsabilizar-se por realizar o processamento da informação antes desta ser apresentada a UI ou realizar alguma operação após alguma interacção com o utilizador.
  • O View e o Presenter nunca comunicam-se directamente mas sim através de interfaces que definem métodos específicos para a comunicação.

Espero que ainda estejamos todos na mesma página e até a 2a(9 de Outubro) feira para mais um post a caminho de efectuar testes em um aplicação android.

Caso tenham alguma dúvida sobre esse post, não hesitem em deixar um comentário que ficarei feliz em esclarecer.

Ate a próxima =)

Se achas que este post foi útil por favor deixe um like clicando no coraçãozinho e partilhe com um amigo!

--

--

Dario Mungoi

Senior Android Developer at @Shopify, Design Sprint Master, former Android GDE