TDC2016POA | Trilha Android - Testes no Android

  • Published on
    13-Apr-2017

  • View
    117

  • Download
    0

Transcript

  • Conceitos, prticas e motivaes

    Testes no Android

  • Eu

    Chapter Lead de Android

    www.rafaeltoledo.net

    twitter.com/_rafaeltoledo

    github.com/rafaeltoledo

    blog.concretesolutions.com.br

  • Por que escrever Testes?

    o dilema do programador mobile

  • Garantir que algo funciona da forma como deveria

    Documentao de comportamento de um sistema

    Garantir que uma mudana no quebra outras partes do app

  • A pirmide refere-se ao desenvolvimento backend

    Front-end em si interface (GUI)

    Ao contrrio dos apps 100% offline, se muitas regras de negcio concentram-se no front-end, sinal que sua arquitetura est errada

  • Por que tenho 2 pastas de Testes?

    o que so as pastas test e androidTest no meu projeto?

  • Testes unitrios / funcionais instrumentados, que necessitam das classes do Android para a execuo.

    So executados em emuladores ou devices reais

    androidTest

  • Testes unitrios executados na JVM (mquina local)

    Componentes externos geralmente so mockados (como as classes do Android)*

    test

  • Testes unitrios executados na JVM (mquina local)

    Componentes externos geralmente so mockados (como as classes do Android)*

    Robolectric

  • escopos de dependncias

    // 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'

  • Bibliotecas e Frameworks

    quem poder nos ajudar?

  • Framework para criao de testes repetveis

    Estrutura da execuo dos testes

    Biblioteca de asseres

    public class MeuTeste {

    @Test public void stuffTest() { Assert.assertEquals(2, 1 + 1); }}

  • Biblioteca para a criao de asseres mais intuitivas e legveis

    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

  • Biblioteca para a criao de asseres mais intuitivas, legveis e fluentes

    Possui uma extenso chamada AssertJ Android feita pela Square

    assertThat(sociedadeDoAnel) .hasSize(9) .contains(frodo, sam) .doesNotContain(sauron);

    // AssertJ AndroidassertThat(view).isGone();

    AssertJ

  • Biblioteca para a criao de mocks

    List mockedList = mock(List.class);

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

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

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

  • Framework para a criao de testes Instrumentados no Android

    Espresso

    AndroidJUnitRunner

    JUnit4 Rules

    UI Automator

    Android TestingSupport Library

  • Espresso

    Biblioteca para a escrita de testes unitrios 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

  • AndroidJUnitRunner

    Suporte ao JUnit 4, acesso a informaes da instrumentao (contexto, execuo, etc.), filtro de testes e distribuio

    Rules

    Possibilita testar Activity, Intent e Service

    UiAutomator

    Testes de UI no Android de forma livre Android TestingSupport Library

  • Robolectric um framework de testes unitrios que desacopla a dependncia 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 execuo do Android

    Testes so instrumentados na prpria JVM

    Robolectric

  • @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

  • Request Matcher

    Biblioteca open-source para a criao de asseres 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

  • Organizao dos testes

    porque teste tambm cdigo

  • Organizao AAA

    Arrange (Organizar): set-up dos testes, preparao dos objetos, etc.

    Act (Agir): a execuo, ou o exerccio do comportamento propriamente dito

    Assert (Confirmao): a verificao se o resultado da execuo foi o esperado

  • Organizao OCA

    Organizar: set-up dos testes, preparao dos objetos, etc.

    Agir: a execuo, ou o exerccio do comportamento propriamente dito

    Confirmao: a verificao se o resultado da execuo foi o esperado

  • Organizao OCA

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

    // Cc.performOperation();

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

  • Cdigo difcil de testartestes podem denunciar problemas no design de classes

  • Design de classes

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

    c.performOperation();

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

  • Design de classes

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

    c.performOperation();

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

  • Por onde comear?

    ok, conheo as ferramentas... mas o que eu testo?

  • Nossa cobaia ser um app que consome a API do StackOverflow e lista os usurios com a maior reputao no site

  • Consumo de API com Retrofit

    RecyclerView com endless scroll

    Salvando dados na mudana de orientao!

  • app/build.gradle

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

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

  • 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'

  • app/build.gradle

    @RunWith(AndroidJUnit4.class)public class HomeActivityTest {

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

    ...}

  • androidTest/. . . /HomeActivityTest.java

    public class HomeActivityTest {

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

  • androidTest/. . . /HomeActivityTest.java

    public class HomeActivityTest {

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

  • Testes e Dependncias Externascomo fazemos com a API? como fazemos com hardware? Como fazemos pra testar?

  • The FIRST Things for Unit Tests

    Fast! (Rpidos): tem que ser executados em alguns milissegundos ou segundos (Android)

    Isolated (Isolados): devem focar em uma poro pequena do cdigo, alinhados com a definio de unitrio

    Repeatable (Repetveis): produzem os mesmos resultados todas as vezes que voc o executa

  • The FIRST Things for Unit Tests

    Self-Validating (Auto-Validados): um teste s um teste se ele se certifica de que as coisas esto certas. Testes no devem ter interao devem poupar e no gastar seu tempo

    Timely (Oportuno): testes, se no se tornarem um hbito, podem facilmente ser esquecidos. E, no futuro, dificilmente esse dbito venha a ser solucionado.

  • Mock

  • 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)

  • Mock objects

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

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

  • Mock objects

    @RunWith(MockitoTestRunner.class)

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

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

    // Mockando callbacks@CaptorCallback> captor;

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

  • Mock Server

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

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

  • Mas...

    Como fazer meu app utilizar esses objetos?

  • Mas...

    Como fazer meu app utilizar esses objetos?

    1. DI / Setter

  • Mas...

    Como fazer meu app utilizar esses objetos?

    1. DI / Setter

    2. Reflection

  • DI / Setter

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

  • DI / Setter

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

    Porm, no bom quando modificamos o nosso cdigo de produo por causa do teste.

    Isso pode vir a gerar problemas na arquitetura ou brechas de segurana

  • DI / Setter

    public class ApiSingleton {

    // ...

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

  • DI / Setter

    public class ApiSingleton {

    // ...

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

    PS: Dagger uma boa sada pra fazer essa troca

  • Reflection

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

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

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

  • Mas pode usar reflection no

    Android? No lento?

  • I Testes unitrios rodam na JVM. No tem esse

    problema.

    II um teste de comportamento no emulador. Alguns

    segundos de setup so aceitveis.

  • Code Coverage

    dados sobre quanto do cdigo est sendo testado

  • Jacoco

    Plugin no Gradle

    Requer algumas configuraes no arquivo de build

  • Notas Finais

    algumas dicas para os navegantes de primeira viagem

  • Test Butler

    Biblioteca + APK para garantir uma execuo mais tranquila dos testes no emulador

    Permite controlar animaes, rede, etc.

    https://github.com/linkedin/test-butler

  • Dicas Finais

    Testes Unitrios != TDD

    Cuidado com os Mocks:

    - No faa mock de tudo- No faa mock de value objects (POJOs)- No faa mock de tipos que voc no tem- Mostre amor pelos seus testes

  • Dicas Finais

    No use flavors para criao de mocks!

    - invasivo ao set-up do projeto- cdigo duplicado- limita a configurao de comportamentos diferentes

    para a mesma unidade de cdigo

  • 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

  • rafaeltoledo.nettwitter.com/_rafaeltoledo

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

    Rio de Janeiro Rua So Jos, 90 cj. 2121Centro (21) 2240-2030

    So Paulo - Rua Sanso Alves dos Santos, 433 4 andar - Brooklin - (11) 4119-0449