Slides usadas en la charla Testeas o k ase?? del Codemotion Spain 2013.
En la sesión se pretende explicar la importancia del testing en dispositivos móviles, así como animar a los desarrolladores a que implementen tests unitarios en sus aplicaciones, concretamente usando el framework Robolectric para Android. Se explicará el funcionamiento de la herramienta, mostrando por qué es tan potente junto con las posibilidades que nos ofrece y, finalmente, se enseñará un ejemplo práctico detallando lo fácil que es tanto configurarlo como empezar a escribir tests.
3. Numerosas apps en los markets = Competencia brutal
Pablo Guardiola
‣
‣
@Guardiola31337 - pguardiola.com
Número de apps Android en el market: 866033
Porcentaje de apps de baja calidad: 23 %
4. Numerosas apps en los markets = Competencia brutal
Pablo Guardiola
‣
‣
@Guardiola31337 - pguardiola.com
Porcentaje de apps con menos de 3 ratings: 43.9 %
Porcentaje de apps entre 0 y 4 estrellas: 65.22 %
5. ¿El código hace exactamente lo que quieres?
Pablo Guardiola
‣
No cabrees a tus usuarios
@Guardiola31337 - pguardiola.com
6. ¿El código hace exactamente lo que quieres?
Pablo Guardiola
@Guardiola31337 - pguardiola.com
7. ¿Podemos reducir el tiempo dedicado a testing usando tests automáticos?
Pablo Guardiola
‣
@Guardiola31337 -pguardiola.com
Testing manual = mucho tiempo
8. ¿Podemos reducir el tiempo dedicado a testing usando tests automáticos?
Pablo Guardiola
‣
@Guardiola31337 - pguardiola.com
Testing automático
9. Vivimos en un mundo ágil
Pablo Guardiola
‣
@Guardiola31337 -pguardiola.com
¡Cuidado con las actualizaciones!
10. Testeas o k ase??
Pablo Guardiola
@Guardiola31337 -pguardiola.com
11. Ventajas de hacer testing
Pablo Guardiola
‣
@Guardiola31337 - pguardiola.com
Incrementa el nivel de confianza que se tiene del
código
‣
Hacer testing “obliga” a escribir código modular
‣
Se desarrolla un producto de calidad
17. ¿Por qué usar Robolectric?
Pablo Guardiola
@Guardiola31337 - pguardiola.com
‣
Es rápido
‣
Interacción con las clases de Android
‣
Incluye HTTP/API testing
‣
Permite invocar métodos privados
‣
Muy fácil de integrar con otros frameworks
18. Además, Robolectric te permite...
Pablo Guardiola
@Guardiola31337 - pguardiola.com
‣
Usar JUnit 4
‣
Iteraciones rápidas
‣
Permite aplicar TDD en el desarrollo
‣
Verificar la lógica y el comportamiento frente a la
implementación
23. Show me the code!
Pablo Guardiola
‣
@Guardiola31337 - pguardiola.com
Se debe indicar con una anotación qué clase es la
encargada de realizar el test
@RunWith(RobolectricTestRunner.class) // <== REQUIRED
for Robolectric!
public class HomeActivityTest {
@Test public void shouldHaveAButtonThatSaysPressMe()
throws Exception {
// test code here
}
}
24. Show me the code!
Pablo Guardiola
‣
@Guardiola31337 - pguardiola.com
En ocasiones Robolectric está oculto...
@RunWith(RobolectricTestRunner.class)
public class HomeActivityTest {
@Test public void shouldHaveHappySmiles() throws
Exception {
String appName = new MyActivity()
.getResources().getString(R.string.app_name);
assertThat(appName, equalTo("SampleRobolectric"));
}
}
‣
¡Escondido! Pero haciendo su trabajo o/
25. Shadows
Pablo Guardiola
‣
@Guardiola31337 - pguardiola.com
Métodos para acceder al estado de los objetos Android
<ImageView
android:id="@+id/pivotal_logo"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:src="@drawable/pivotallabs_logo"
android:layout_marginBottom="10dip"
>
@Test
public void shouldHaveALogo() throws Exception {
ImageView pivotalLogo =
(ImageView)activity.findViewById(R.id.pivotal_logo);
ShadowImageView shadowPivotalLogo =
Robolectric.shadowOf(pivotalLogo);
assertThat(shadowPivotalLogo.resourceId,
equalTo(R.drawable.pivotallabs_logo));
}
‣
Acceso a estado y variables, de otro modo sería imposible
26. HTTP testing
Pablo Guardiola
@Guardiola31337 - pguardiola.com
@RunWith(RobolectricTestRunner.class)
public class HttpTest {
@Test public void
testGet_FormsCorrectRequest_noBasicAuth() throws
Exception {
Robolectric.addPendingHttpResponse(200, "OK");
new Http().get("www.codemotion.es",
Maps.<String, String>newHashMap(),
null,
null);
assertThat(((HttpUriRequest)
Robolectric.getSentHttpRequest(0)).getURI(),
equalTo(URI.create("www.codemotion.es")));
}
}
27. HTTP testing
Pablo Guardiola
@Guardiola31337 - pguardiola.com
Robolectric.addPendingHttpResponse(200, "OK");
‣
‣
Robolectric hace de mock server, necesita saber cómo
responder a la llamada para evitar RuntimeException
Respuesta por defecto compartida
@RunWith(RobolectricTestRunner.class)
public class HttpTest {
@Before public void setup() {
Robolectric.addPendingHttpResponse(200, "OK");
}
...
}
28. HTTP testing
Pablo Guardiola
‣
@Guardiola31337 - pguardiola.com
Otros ejemplos
@Test public void shouldReturnCorrectResponse() throws
Exception {
Robolectric
.addPendingHttpResponse(666, "it's all cool");
Http.Response response = http.get("www.example.com",
new HashMap<String, String>(), null, null);
assertThat(fromStream(response.getResponseBody()),
equalTo("it's all cool"));
assertThat(response.getStatusCode(), equalTo(666));
}
}
‣
Métodos correctos, cabeceras...
29. Extendiendo Robolectric
Pablo Guardiola
‣
‣
@Guardiola31337 - pguardiola.com
Robolectric no tiene funcionalidad para todo
Robolectric permite extender las clases Shadow
@Implements
@Implementation
Robolectric.getDefaultShadowClasses()
__constructo__
@RealObject
31. ¿Por qué no usar directamente el código Android?
Pablo Guardiola
‣
‣
‣
@Guardiola31337 - pguardiola.com
Difícil conseguir los .jar reales
Difícil trabajar con código nativo
Difícil dejar de usar las clases Shadow
34. Robolectric 2.0
Pablo Guardiola
‣
‣
‣
@Guardiola31337 - pguardiola.com
El paquete de soporte no usa clases Shadow
Fragments, action bar, loaders usan su implementación
real
Tests tendrán mismo comportamiento que en los
dispositivos
35. Robolectric 2.0
Pablo Guardiola
@Guardiola31337 - pguardiola.com
‣
Se pueden testear estilos y temas
‣
Acceso a los recursos del sistema
‣
Soporte ActionBarSherlock
‣
Mejoras de rendimiento y corrección de bugs
36. Robolectric 2.0
Pablo Guardiola
‣
@Guardiola31337 - pguardiola.com
Testing bajo diferentes configuraciones del dispositivo
@Test @Config("en")
public void shouldGiveEnglishResponses() throws Exception {
assertThat(activity.findViewById(R.id.message))
.hasText("Hello!");
}
}
@Test @Config("fr")
public void shouldGiveFrenchResponses throws Exception {
assertThat(activity.findViewById(R.id.message))
.hasText("Bonjour!");
}
}
37. Robolectric 2.0
Pablo Guardiola
‣
@Guardiola31337 - pguardiola.com
ActivityController mayor control sobre el ciclo de vida
FooActivity foo = Robolectric.buildActivity(FooActivity.class)
.create().start().resume().get();
ActivityController<FooActivity> fooController =
Robolectric.buildActivity(FooActivity.class).create();
FooActivity foo = fooController.get();
// Ensure the user was injected.
assertThat(foo.user).isNotNull();
// Log the user out.
foo.user.expireSession();
fooController.start().resume();
// Ensure the activity is now finishing.
assertThat(foo).isFinishing();
38. Robolectric 2.0
Pablo Guardiola
‣
‣
‣
‣
‣
@Guardiola31337 - pguardiola.com
El uso del SDK de Android está aumentando
considerablemente
Todo el mundo puede contribuir:
https://github.com/robolectric/robolectric
http://robolectric.org/
http://robolectric.blogspot.com.es/