Android: testes automatizados e TDD

Post on 29-Jun-2015

1683 Views

Category:

Technology

4 Downloads

Preview:

Click to see full reader

DESCRIPTION

Palestra ministrada por Ivan de Aguirre no AndoidDay 2013.

Transcript

Android: Testes Automatizados e TDD

Ivan de Aguirre@IvAguirre

ivan.aguirre@dextra-sw.com

Agenda

• Conceitos e Terminologia.• Overview do Framework de

testes.• O que testar?• TDD

Conceitos e Terminologia

• Teste isolado de uma unidade do software.• Unidade precisa ser testável.• Uso de mocks, stubs e fakes.• Execução rápida.• Teste Unitário Clássico: unidade = classe.• TDD: unidade = comportamento.

Teste Unitário

• Teste isolado de uma unidade do software.• Unidade precisa ser testável.• Uso de mocks, stubs e fakes.• Execução rápida.• Teste Unitário Clássico: unidade = classe.• TDD: unidade = comportamento.

Teste Unitário

You can't have unit tests if you don't have units.@CompSciFact

Teste de API• Testes de endpoint.• Sistema integrado.• Asserções são mais difíceis.• Testes mais difíceis de manter.• Execução mais lenta.• Ex.: método de uma classe de

serviço, chamada à um webservice.

• Black Box.• Execução manual.• É o que o tester faz.

Teste Funcional

Teste de UI• Simula uma execução de teste

funcional.• Difíceis de manter.• Execução lenta.

Testes Automatizados

• Integrados à build contínua.• Feedback constante.• Todos os testes devem estar

passando sempre.

Não elimina a necessidade de testes manuais.

Afinal, o que esperamos ao escrever testes?

Afinal, o que esperamos ao escrever testes?

Feedback

Afinal, o que esperamos ao escrever testes?

QualidadeFeedback

Afinal, o que esperamos ao escrever testes?

QualidadeFeedback

Evitar bugs

Afinal, o que esperamos ao escrever testes?

QualidadeFeedback

Evitar bugs Agilidade

Afinal, o que esperamos ao escrever testes?

QualidadeFeedback

Evitar bugs

Confiança

Agilidade

Framework

• Classe base: AndroidTestCase• Especializações:o ApplicationTestCaseo LoaderTestCaseo ServiceTestCaseo ProviderTestCase2

Testes Unitários

public class AppProviderTestCase extends ProviderTestCase2<DataProvider> { private MockContentResolver mMockResolver;

public AppProviderTestCase() { super(DataProvider.class, Question.AUTHORITY); } @Override protected void setUp() throws Exception { super.setUp(); mMockResolver = getMockContentResolver(); }

public void testInsertAndRetrieveQuestions2() { Question q = new Question(1L, "Concorda ?"); q.addRecord(getMockContext()); Cursor c = mMockResolver.query( Question.CONTENT_URI, null, null, null, null); Question retrieved = new Question(c); assertEquals(1, c.getCount()); assertEquals(q.getId(), retrieved.getId()); assertEquals(q.getQuestionText(), retrieved.getQuestionText());}

• Classe base: InstrumentationTestCase

• Especializações:o ActivityTestCase

§ ActivityUnitTestCase (onCreate).§ ActivityInstrumentationTestCase2

(ciclo completo).

Testes com Instrumentação

public class QuestionActivityTestCase extends ActivityUnitTestCase<QuestionActivity> { ... public void testIntent() { Intent intent = new Intent(); String question = "Pergunta !!!"; intent.putExtra(Question.Columns.QUESTIONTEXT, question); Activity activity = startActivity(intent, null, null); TextView view = (TextView) activity.findViewById( R.id.question_text); assertEquals(question, view.getText()); }}

public class MainActivityTestCase extendsActivityInstrumentationTestCase2<MainActivity> {

public MainActivityTestCase() { super(MainActivity.class); }

public void testMainStarts() { assertNotNull("Activity nao foi criada!", getActivity()); }}

Instrumentation in = getInstrumentation();

Ao trabalhar com eventos (click, touch, etc..) cuidado com atrasos da Thread de UI.

• uiautomatorview (android_sdk/tools): detecta Views acessíveis pelo automator e "NAF" nodes.

• uiautomator:o http://developer.android.com/tools/help/uiautomator/

index.htmlo android:contentDescriptor:

ImageButton, ImageView, CheckBox.

o android:hint: EditText.

Testes de UI

Que testes escrever?

O que disse Kent Beck?

I get paid for code that works, not for tests, so my philosophy is to test as little as possible to reach a given level of confidence (I suspect this level of confidence is high compared to industry standards, but that could just be hubris). If I don't typically make a kind of mistake (like setting the wrong variables in a constructor), I don't test for it. I do tend to make sense of test errors, so I'm extra careful when I have logic with complicated conditionals. When coding on a team, I modify my strategy to carefully test code that we, collectively, tend to get wrong.

Different people will have different testing strategies based on this philosophy, but that seems reasonable to me given the immature state of understanding of how tests can best fit into the inner loop of coding. Ten or twenty years from now we'll likely have a more universal theory of which tests to write, which tests not to write, and how to tell the difference. In the meantime, experimentation seems in order.

Android Facts

• Receitas de bolo ("boilerplate code").

• Emulador é uma beleza. Só que não...

• Certas classes do framework são difíceis de mockar.

• Sistema externo pode complicar os testes.

• Asserções “visuais“ são díficeis de automatizar.

• Mesmo testes unitários demoram pra executar.

Android Facts

• Você não precisa testar a API do Android.• Se necessário crie unidades testáveis fora dos

componentes Android e isole o boilerplate code.• Difícil de testar de forma automatizada: execução

assíncrona, layouts, dependência de sistema remoto, dependência de outras aplicações, boilerplate code, reação à "condições adversas", etc.

Então: que testes escrever?

• Classes difíceis de mockar: crie um wrapper.

• Content Providers são fáceis de testar.• Testes funcionais automatizados podem ser

difíceis de manter.• Testes difíceis de escrever ou de manter

podem indicar um problema de design.• Instrumentação no lugar de testes de UI

automatizados.

Dicas

• Testes de sanidade ajudam a evitar erros.Ex.: apenas iniciar uma Activity.

• É possível testar execução "assíncrona" de forma unitária, por ex.: https://android.googlesource.com/platform/frameworks/base/+/master/test-runner/src/android/test/LoaderTestCase.java

• Teste de Stress com Monkey:http://developer.android.com/tools/help/monkey.html

Dicas

Exemplo: SincronizaçãoAuthenticator.javaAuthenticatorService.javaSyncAdapter.javaSyncService.javaauthenticator.xmlsyncadapter.xml

public class SyncAdapter extends AbstractThreadedSyncAdapter{ ... @Override public void onPerformSync(...) {

}}

Código que faz a Sincronização

public class SyncAdapter extends AbstractThreadedSyncAdapter{ ... @Override public void onPerformSync(...) {

}}

Código que faz a Sincronização

SyncAdapter é difícil de testar com testes

unitários!!

public class SyncAdapter extends AbstractThreadedSyncAdapter{ ... @Override public void onPerformSync(...) {

}}

public class SyncAdapter extends AbstractThreadedSyncAdapter{ ... @Override public void onPerformSync(...) {

}}

new QuestionSync(getContext()) .doSync();

public class SyncAdapter extends AbstractThreadedSyncAdapter{ ... @Override public void onPerformSync(...) {

}}

new QuestionSync(getContext()) .doSync();

QuestionSync é uma classe Java comum. É mais fácil testá-la!!

public class QuestionSyncTest extends ProviderTestCase2<DataProvider> { ... public void testReceiveOneQuestionWhenDataBaseIsEmpty() { Cursor c = mMockResolver.query(Question.CONTENT_URI, null, null, null, null); assertEquals(0, c.getCount()); String json = "[{\"id\":1, \"question\": \"nova pergunta\"}]"; getSyncForWebServicesReturn(json).doSync(); c = mMockResolver.query(Question.CONTENT_URI, null, null, null, null); assertEquals(1, c.getCount()); }}

private QuestionSync getSyncForWebServicesReturn(final String wsReturn) {

final QuestionRemoteRepository mock = new QuestionRemoteRepository() { @Override protected String fetchAll() { return wsReturn; } }; return new QuestionSync(getMockContext()) { @Override public void doSync() { this.questionsRepository = mock; super.doSync(); } };}

TDD

while (true) { Adicione um novo teste. Faça o teste compilar. Execute o teste: vai falhar !! Implemente da forma mais simples possível para o teste passar. Refatore e mantenha os testes verdes. }

TDD - Sugestão de Workflow• Execução dos testes é muito lenta para

TDD de fato.

• Crie unidades com TDD (ou algo parecido

com isso).

• Depois de alguma iterações adicione

componentes Android.

Perguntas?

Muito Obrigado!

Ivan de Aguirre@IvAguirre

ivan.aguirre@dextra-sw.com

top related