TDC2016POA | Trilha Android - Testes no Android

71
Conceitos, práticas e motivações Testes no Android

Transcript of TDC2016POA | Trilha Android - Testes no Android

Page 1: TDC2016POA | Trilha Android - Testes no Android

Conceitos, práticas e motivações

Testes no Android

Page 2: TDC2016POA | Trilha Android - Testes no Android

Eu

Chapter Lead de Android

www.rafaeltoledo.net

twitter.com/_rafaeltoledo

github.com/rafaeltoledo

blog.concretesolutions.com.br

Page 3: TDC2016POA | Trilha Android - Testes no Android

Por que escrever Testes?

o dilema do programador mobile

Page 4: TDC2016POA | Trilha Android - Testes no Android

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

Page 5: TDC2016POA | Trilha Android - Testes no Android
Page 6: TDC2016POA | Trilha Android - Testes no Android
Page 7: TDC2016POA | Trilha Android - Testes no Android

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

Page 8: TDC2016POA | Trilha Android - Testes no Android

Por que tenho 2 pastas de Testes?

o que são as pastas test e androidTest no meu projeto?

Page 9: TDC2016POA | Trilha Android - Testes no Android

Testes unitários / funcionais instrumentados, que necessitam das classes do Android para a execução.

São executados em emuladores ou devices reais

androidTest

Page 10: TDC2016POA | Trilha Android - Testes no Android

Testes unitários executados na JVM (máquina local)

Componentes externos geralmente são mockados (como as classes do Android)*

test

Page 11: TDC2016POA | Trilha Android - Testes no Android

Testes unitários executados na JVM (máquina local)

Componentes externos geralmente são mockados (como as classes do Android)*

Robolectric

Page 12: TDC2016POA | Trilha Android - Testes no Android

escopos de dependências

// somente testtestCompile 'junit:junit:4.12'testCompile 'org.robolectric:robolectric:3.1.2'

// somente androidTestandroidTestCompile 'com.android.support.test:runner:0.5'androidTestCompile 'com.android.support.test:rules:0.5'

Page 13: TDC2016POA | Trilha Android - Testes no Android

Bibliotecas e Frameworks

quem poderá nos ajudar?

Page 14: TDC2016POA | Trilha Android - Testes no Android

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); }}

Page 15: TDC2016POA | Trilha Android - Testes no Android

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

Page 16: TDC2016POA | Trilha Android - Testes no Android

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 AndroidassertThat(view).isGone();

AssertJ

Page 17: TDC2016POA | Trilha Android - Testes no Android

Biblioteca para a criação de mocks

List mockedList = mock(List.class);

mockedList.add("one");mockedList.clear();

verify(mockedList).add("one");verify(mockedList).clear();

Page 18: TDC2016POA | Trilha Android - Testes no Android

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 comportamentoSystem.out.println(mockedList.get(999));

Page 19: TDC2016POA | Trilha Android - Testes no Android

Framework para a criação de testes Instrumentados no Android

Espresso

AndroidJUnitRunner

JUnit4 Rules

UI Automator

Android TestingSupport Library

Page 20: TDC2016POA | Trilha Android - Testes no Android

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 TestingSupport Library

Page 21: TDC2016POA | Trilha Android - Testes no Android

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 TestingSupport Library

Page 22: TDC2016POA | Trilha Android - Testes no Android

“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

Page 23: TDC2016POA | Trilha Android - Testes no Android

@Testpublic void clickingButton_shouldChangeResultsViewText() {

MyActivity activity = Robolectric.setupActivity(MyActivity.class);

Button button = (Button) activity.findViewById(R.id.button); TextView results = (TextView) activity.findViewById(R.id.results);

button.performClick(); assertThat(results.getText().toString()).isEqualTo("Hello!");}

Robolectric

Page 24: TDC2016POA | Trilha Android - Testes no Android

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

Page 25: TDC2016POA | Trilha Android - Testes no Android

Organização dos testes

porque teste também é código

Page 26: TDC2016POA | Trilha Android - Testes no Android

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

Page 27: TDC2016POA | Trilha Android - Testes no Android

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

Page 28: TDC2016POA | Trilha Android - Testes no Android

Organização OCA

// OCalculator c = new Calculator();c.setFirstNumber(1);c.setSecondNumber(2);c.setOperation(Calculador.SUM);

// Cc.performOperation();

// AassertThat(c.getResult(), is(3));

Page 29: TDC2016POA | Trilha Android - Testes no Android

Código difícil de testartestes podem denunciar problemas no design de classes

Page 30: TDC2016POA | Trilha Android - Testes no Android

Design de classes

Calculator c = new Calculator();c.setFirstNumber(1);c.setSecondNumber(2);c.setOperation(Calculador.SUM);

c.performOperation();

assertThat(c.getResult(), is(3));

Page 31: TDC2016POA | Trilha Android - Testes no Android

Design de classes

Calculator c = new Calculator.Builder() .firstNumber(1) .secondNumber(2) .operation(Calculador.SUM) .build();

c.performOperation();

assertThat(c.getResult(), is(3));

Page 32: TDC2016POA | Trilha Android - Testes no Android

Por onde começar?

ok, conheço as ferramentas... mas o que eu testo?

Page 33: TDC2016POA | Trilha Android - Testes no Android

Nossa cobaia será um app que consome a API do StackOverflow e lista os usuários com a maior reputação no site

Page 34: TDC2016POA | Trilha Android - Testes no Android

Consumo de API com Retrofit

RecyclerView com endless scroll

Salvando dados na mudança de orientação!

Page 35: TDC2016POA | Trilha Android - Testes no Android

app/build.gradle

defaultConfig { applicationId 'net.rafaeltoledo.tests' minSdkVersion 16 targetSdkVersion 23 versionCode 1 versionName '0.0.1'

testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'}

Page 36: TDC2016POA | Trilha Android - Testes no Android

app/build.gradle

androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'androidTestCompile 'com.android.support.test.espresso:espresso-contrib:2.2.2'androidTestCompile 'com.android.support.test:runner:0.5'androidTestCompile 'com.android.support.test:rules:0.5'

Page 37: TDC2016POA | Trilha Android - Testes no Android

app/build.gradle

@RunWith(AndroidJUnit4.class)public class HomeActivityTest {

@Rule public ActivityTestRule<HomeActivity> activityRule = new ActivityTestRule<>(HomeActivity.class);

...}

Page 38: TDC2016POA | Trilha Android - Testes no Android

androidTest/. . . /HomeActivityTest.java

public class HomeActivityTest {

@Test public void checkIfRecyclerViewIsLoading() { // ... }}

Page 39: TDC2016POA | Trilha Android - Testes no Android

androidTest/. . . /HomeActivityTest.java

public class HomeActivityTest {

@Test public void checkIfRecyclerViewIsLoading() { // êpa! Calma aí... }}

Page 40: TDC2016POA | Trilha Android - Testes no Android

Testes e Dependências Externascomo fazemos com a API? como fazemos com hardware? Como fazemos pra testar?

Page 41: TDC2016POA | Trilha Android - Testes no Android
Page 42: TDC2016POA | Trilha Android - Testes no Android
Page 43: TDC2016POA | Trilha Android - Testes no Android
Page 44: TDC2016POA | Trilha Android - Testes no Android
Page 45: TDC2016POA | Trilha Android - Testes no Android
Page 46: TDC2016POA | Trilha Android - Testes no Android

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

Page 47: TDC2016POA | Trilha Android - Testes no Android

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.

Page 48: TDC2016POA | Trilha Android - Testes no Android

Mock

Page 49: TDC2016POA | Trilha Android - Testes no Android

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)

Page 50: TDC2016POA | Trilha Android - Testes no Android

Mock objects

// Mockando a API com o mockitoStackApi api = mock(StackApi.class);when(api.getUsers(anyInt()).thenReturn(createMockedResponse());

// Mockando callbacksArgumentCaptor<Callback<ApiCollection<User>>> captor = forClass(Callback.class);verify(api.getUsersAsync(anyInt(), captor);captor.getValue().onSuccess(createMockedCall(), createMockedResponse());

Page 51: TDC2016POA | Trilha Android - Testes no Android

Mock objects

@RunWith(MockitoTestRunner.class)

// Mockando a API com o mockito@MockStackApi api;

when(api.getUsers(anyInt()).thenReturn(createMockedResponse);

// Mockando callbacks@CaptorCallback<ApiCollection<User>>> captor;

verify(api.getUsersAsync(anyInt(), captor);captor.getValue().onSuccess(createMockedCall(), createMockedResponse());

Page 52: TDC2016POA | Trilha Android - Testes no Android

Mock Server

// Mockando a API com o mockitoMockWebServer server = new MockWebServer();

server.enqueue(new MockResponse() .setBody(json) // string! .addHeader("Header", "value") .setResponseCode(200));

Page 53: TDC2016POA | Trilha Android - Testes no Android

Mas...

Como fazer meu app utilizar esses objetos?

Page 54: TDC2016POA | Trilha Android - Testes no Android

Mas...

Como fazer meu app utilizar esses objetos?

1. DI / Setter

Page 55: TDC2016POA | Trilha Android - Testes no Android

Mas...

Como fazer meu app utilizar esses objetos?

1. DI / Setter

2. Reflection

Page 56: TDC2016POA | Trilha Android - Testes no Android

DI / Setter

// API Mockada que criamos :)apiSingleton.setApi(mockApiObject);

Page 57: TDC2016POA | Trilha Android - Testes no Android

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

Page 58: TDC2016POA | Trilha Android - Testes no Android

DI / Setter

public class ApiSingleton {

// ...

@VisibleForTesting public void setApi(MyApiInterface api) { this.api = api; }}

Page 59: TDC2016POA | Trilha Android - Testes no Android

DI / Setter

public class ApiSingleton {

// ...

@VisibleForTesting public void setApi(MyApiInterface api) { this.api = api; }}

PS: Dagger é uma boa saída pra fazer essa troca

Page 60: TDC2016POA | Trilha Android - Testes no Android

Reflection

// Mudamos o valor do SingletonApiModule module = ApiModule.getInstance();

Field field = module.getClass().getDeclaredField("api");field.setAccessible(true);field.set(module, mockApi);

Page 61: TDC2016POA | Trilha Android - Testes no Android

Reflection

// Mudamos o valor do SingletonApiModule 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);

Page 62: TDC2016POA | Trilha Android - Testes no Android

Mas pode usar reflection no

Android? Não é lento?

Page 63: TDC2016POA | Trilha Android - Testes no Android

I – Testes unitários rodam na JVM. Não tem esse

problema.

II – É um teste de comportamento no emulador. Alguns

segundos de setup são aceitáveis.

Page 64: TDC2016POA | Trilha Android - Testes no Android

Code Coverage

dados sobre quanto do código está sendo testado

Page 65: TDC2016POA | Trilha Android - Testes no Android

Jacoco

Plugin no Gradle

Requer algumas configurações no arquivo de build

Page 66: TDC2016POA | Trilha Android - Testes no Android

Notas Finais

algumas dicas para os navegantes de primeira viagem

Page 67: TDC2016POA | Trilha Android - Testes no Android

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

Page 68: TDC2016POA | Trilha Android - Testes no Android

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

Page 69: TDC2016POA | Trilha Android - Testes no Android

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

Page 70: TDC2016POA | Trilha Android - Testes no Android

Links

google.github.io/android-testing-support-library

github.com/googlesamples/android-testing

blog.sqisland.com

github.com/googlesamples/android-topeka

github.com/chiuki/friendspell

github.com/concretesolutions/requestmatcher

github.com/rafaeltoledo/android-keep-testing

Page 71: TDC2016POA | Trilha Android - Testes no Android

rafaeltoledo.nettwitter.com/_rafaeltoledo

blog.concretesolutions.com.brconcretesolutions.com.br/carreira

Rio de Janeiro – Rua São José, 90 – cj. 2121Centro – (21) 2240-2030

São Paulo - Rua Sansão Alves dos Santos, 433 4º andar - Brooklin - (11) 4119-0449