O documento discute testes no Android, abordando conceitos como mocks, frameworks de teste, organização de testes e como testar dependências externas. Ele também fornece dicas sobre por onde começar a escrever testes e como garantir uma execução tranquila.
4. Garantir que algo funciona da
forma como deveria
Documentação de
comportamento de um sistema
Garantir que uma mudança não
quebra outras partes do app
5.
6.
7. A pirâmide refere-se ao
desenvolvimento backend
Front-end em si é interface (GUI)
Ao contrário dos apps 100%
offline, se muitas regras de
negócio concentram-se no
front-end, é sinal que sua
arquitetura está errada
8. Por que tenho 2 pastas de Testes?
o que são as pastas test e androidTest no meu projeto?
9. Testes unitários / funcionais
instrumentados, que necessitam das
classes do Android para a execução.
São executados em emuladores ou
devices reais
androidTest
10. Testes unitários executados na JVM
(máquina local)
Componentes externos geralmente
são mockados (como as classes do
Android)*
test
11. Testes unitários executados na JVM
(máquina local)
Componentes externos geralmente
são mockados (como as classes do
Android)*
Robolectric
12. escopos de dependências
// somente test
testCompile 'junit:junit:4.12'
testCompile 'org.robolectric:robolectric:3.1.2'
// somente androidTest
androidTestCompile 'com.android.support.test:runner:0.5'
androidTestCompile 'com.android.support.test:rules:0.5'
14. Framework para criação de testes ‘repetíveis’
Estrutura da execução dos testes
Biblioteca de asserções
public class MeuTeste {
@Test
public void stuffTest() {
Assert.assertEquals(2, 1 + 1);
}
}
15. Biblioteca para a criação de asserções mais intuitivas e legíveis
Se tornou parte do JUnit
assertThat(1 + 1, is(2));
assertThat(lista, contains(2, 3, 4, 8);
String texto = "Android no TDC"
assertThat(texto, containsString("Android");
assertThat(texto, not(containsString("iOS");
Hamcrest
16. Biblioteca para a criação de asserções mais intuitivas, legíveis e
fluentes
Possui uma extensão chamada AssertJ Android feita pela Square
assertThat(sociedadeDoAnel)
.hasSize(9)
.contains(frodo, sam)
.doesNotContain(sauron);
// AssertJ Android
assertThat(view).isGone();
AssertJ
17. Biblioteca para a criação de mocks
List mockedList = mock(List.class);
mockedList.add("one");
mockedList.clear();
verify(mockedList).add("one");
verify(mockedList).clear();
18. LinkedList mockedList = mock(LinkedList.class);
when(mockedList.get(0)).thenReturn("first");
// Vai mostrar "first"
System.out.println(mockedList.get(0));
// Vai mostrar null, já que não mockamos o comportamento
System.out.println(mockedList.get(999));
19. Framework para a criação de testes Instrumentados no Android
Espresso
AndroidJUnitRunner
JUnit4 Rules
UI Automator
Android Testing
Support Library
20. Espresso
Biblioteca para a escrita de testes unitários de UI para o
Android
onView(withId(R.id.name_field)).perform(typeText("TDC"));
onView(withId(R.id.greet_button)).perform(click());
onView(withText("Olá, TDC!")).check(matches(isDisplayed());
Android Testing
Support Library
21. AndroidJUnitRunner
Suporte ao JUnit 4, acesso a informações da instrumentação (contexto,
execução, etc.), filtro de testes e distribuição
Rules
Possibilita testar Activity, Intent e Service
UiAutomator
Testes de UI no Android de forma “livre” Android Testing
Support Library
22. “Robolectric é um framework de testes unitários que desacopla a
dependência do jar do Android, de forma que você possa fazer o
desenvolvimento do seu aplicativo guiado por testes. Execute seus
testes na JVM em segundos!”
É um simulador do ambiente de execução do Android
Testes são “instrumentados” na própria JVM
Robolectric
24. Request Matcher
Biblioteca open-source para a criação de asserções das requests do
app, utilizando em conjunto o Mock Web Server da Square
serverRule.enqueue(200, "body.json")
.assertPathIs("/somepath")
.assertNoBody()
.assertMethodIs(RequestMatcher.GET);
github.com/concretesolutions/requestmatcher
26. Organização AAA
Arrange (Organizar): set-up dos testes, preparação dos
objetos, etc.
Act (Agir): a execução, ou o exercício do comportamento
propriamente dito
Assert (Confirmação): a verificação se o resultado da
execução foi o esperado
27. Organização OCA
Organizar: set-up dos testes, preparação dos objetos, etc.
Agir: a execução, ou o exercício do comportamento
propriamente dito
Confirmação: a verificação se o resultado da execução foi o
esperado
28. Organização OCA
// O
Calculator c = new Calculator();
c.setFirstNumber(1);
c.setSecondNumber(2);
c.setOperation(Calculador.SUM);
// C
c.performOperation();
// A
assertThat(c.getResult(), is(3));
29. Código difícil de testar
testes podem denunciar problemas no design de
classes
30. Design de classes
Calculator c = new Calculator();
c.setFirstNumber(1);
c.setSecondNumber(2);
c.setOperation(Calculador.SUM);
c.performOperation();
assertThat(c.getResult(), is(3));
31. Design de classes
Calculator c = new Calculator.Builder()
.firstNumber(1)
.secondNumber(2)
.operation(Calculador.SUM)
.build();
c.performOperation();
assertThat(c.getResult(), is(3));
38. androidTest/. . . /HomeActivityTest.java
public class HomeActivityTest {
@Test
public void checkIfRecyclerViewIsLoading() {
// ...
}
}
39. androidTest/. . . /HomeActivityTest.java
public class HomeActivityTest {
@Test
public void checkIfRecyclerViewIsLoading() {
// êpa! Calma aí...
}
}
40. Testes e Dependências Externas
como fazemos com a API? como fazemos com
hardware? Como fazemos pra testar?
41.
42.
43.
44.
45.
46. The FIRST Things for Unit Tests
Fast! (Rápidos): tem que ser executados em alguns
milissegundos ou segundos (Android)
Isolated (Isolados): devem focar em uma porção pequena
do código, alinhados com a definição de unitário
Repeatable (Repetíveis): produzem os mesmos resultados
todas as vezes que você o executa
47. The FIRST Things for Unit Tests
Self-Validating (Auto-Validados): um teste só é um teste se
ele se certifica de que as coisas estão certas. Testes não
devem ter interação – devem poupar e não gastar seu
tempo
Timely (Oportuno): testes, se não se tornarem um hábito,
podem facilmente ser “esquecidos”. E, no futuro,
dificilmente esse débito venha a ser solucionado.
49. Abordagens de Mock
Mock Objects: podemos programar o comportamento dos
objetos para que respondam como desejamos (Mockito)
Mock Requests: deixamos que os objetos se comportem
normalmente e somente apontamos para uma outra API
(MockWebServer)
50. Mock objects
// Mockando a API com o mockito
StackApi api = mock(StackApi.class);
when(api.getUsers(anyInt()).thenReturn(createMockedResponse());
// Mockando callbacks
ArgumentCaptor<Callback<ApiCollection<User>>> captor =
forClass(Callback.class);
verify(api.getUsersAsync(anyInt(), captor);
captor.getValue().onSuccess(createMockedCall(), createMockedResponse());
51. Mock objects
@RunWith(MockitoTestRunner.class)
// Mockando a API com o mockito
@Mock
StackApi api;
when(api.getUsers(anyInt()).thenReturn(createMockedResponse);
// Mockando callbacks
@Captor
Callback<ApiCollection<User>>> captor;
verify(api.getUsersAsync(anyInt(), captor);
captor.getValue().onSuccess(createMockedCall(), createMockedResponse());
52. Mock Server
// Mockando a API com o mockito
MockWebServer server = new MockWebServer();
server.enqueue(new MockResponse()
.setBody(json) // string!
.addHeader("Header", "value")
.setResponseCode(200));
56. DI / Setter
// API Mockada que criamos :)
apiSingleton.setApi(mockApiObject);
57. DI / Setter
// API Mockada que criamos :)
apiSingleton.setApi(mockApiObject);
Porém, não é bom quando modificamos o nosso código de
produção por causa do teste.
Isso pode vir a gerar problemas na arquitetura ou brechas
de segurança
58. DI / Setter
public class ApiSingleton {
// ...
@VisibleForTesting
public void setApi(MyApiInterface api) {
this.api = api;
}
}
59. DI / Setter
public class ApiSingleton {
// ...
@VisibleForTesting
public void setApi(MyApiInterface api) {
this.api = api;
}
}
PS: Dagger é uma boa saída pra fazer essa troca
60. Reflection
// Mudamos o valor do Singleton
ApiModule module = ApiModule.getInstance();
Field field = module.getClass().getDeclaredField("api");
field.setAccessible(true);
field.set(module, mockApi);
61. Reflection
// Mudamos o valor do Singleton
ApiModule module = ApiModule.getInstance();
Field field = module.getClass().getDeclaredField("api");
field.setAccessible(true);
field.set(module, mockApi);
// testCompile 'net.vidageek:mirror:1.6.1'
new Mirror().on(module).set().field("api").withValue(mockApi);
67. Test Butler
Biblioteca + APK para garantir uma execução mais tranquila
dos testes no emulador
Permite controlar animações, rede, etc.
https://github.com/linkedin/test-butler
68. Dicas Finais
Testes Unitários != TDD
Cuidado com os Mocks:
- Não faça mock de tudo
- Não faça mock de value objects (POJOs)
- Não faça mock de tipos que você não tem
- Mostre amor pelos seus testes <3
69. Dicas Finais
Não use flavors para criação de mocks!
- invasivo ao set-up do projeto
- código duplicado
- limita a configuração de comportamentos diferentes
para a mesma unidade de código