4. 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.
5. 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.
You can't have unit tests if you don't have units.
@CompSciFact
6. •
•
•
•
•
•
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.
9. Testes Automatizados
• Integrados à build contínua.
• Feedback constante.
• Todos os testes devem estar
passando sempre.
Não elimina a necessidade de testes manuais.
17. Testes Unitários
• Classe base: AndroidTestCase
• Especializações:
o ApplicationTestCase
o LoaderTestCase
o ServiceTestCase
o ProviderTestCase2
18. 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();
}
19. 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());
}
20. Testes com Instrumentação
• Classe base:
InstrumentationTestCase
• Especializações:
o ActivityTestCase
§ ActivityUnitTestCase (onCreate).
§ ActivityInstrumentationTestCase2
(ciclo completo).
21. 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());
}
}
22. public class MainActivityTestCase extends
ActivityInstrumentationTestCase2<MainActivity> {
public MainActivityTestCase() {
super(MainActivity.class);
}
public void testMainStarts() {
assertNotNull("Activity nao foi criada!",
getActivity());
}
}
24. •
•
Testes 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.html
android:contentDescriptor:
ImageButton, ImageView,
CheckBox.
o android:hint: EditText.
o
27. 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.
28. 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.
30. 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.
31. Então: que testes escrever?
•
•
•
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.
32. Dicas
• 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.
33. 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
35. public class SyncAdapter extends
AbstractThreadedSyncAdapter{
...
@Override
public void onPerformSync(...) {
Código que faz a Sincronização
}
}
36. 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!!
37. public class SyncAdapter extends
AbstractThreadedSyncAdapter{
...
@Override
public void onPerformSync(...) {
}
}
38. public class SyncAdapter extends
AbstractThreadedSyncAdapter{
...
@Override
public void onPerformSync(...) {
new QuestionSync(getContext())
.doSync();
}
}
39. public class SyncAdapter extends
AbstractThreadedSyncAdapter{ é uma
QuestionSync
classe Java comum. É
...
mais fácil testá-la!!
@Override
public void onPerformSync(...) {
new QuestionSync(getContext())
.doSync();
}
}
40. 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());
}
}
41. 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();
}
};
44. 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.
}
45. 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.