Showing posts with label Español. Show all posts
Showing posts with label Español. Show all posts

Tuesday, January 3, 2017

Proceso de Unit Testing

Hablemos de Unit Testing. Este tema reaparece cada par de semanas en grupos de Android, por lo que parece interesante hablar de él.

Cuando se habla de Unit Testing existen varias opciones, como unit testing, integration testing, UI testing… todas son opciones válidas, pero en este post voy a hablar de qué es Unit Testing en general, y el por qué quieres tener unit testing en tu aplicación.

Voy a explicar esto con escenarios de experiencias que me han ocurrido en mi carrera.

Tu nueva aplicación - Dia 1.

Digamos que comienzas a hacer una aplicación nueva, y tu eres el único desarrollador en el proyecto. Esta aplicación es una aplicación bancaria, que solo te deja crear una cuenta nueva en el banco, y ver el saldo de tu cuenta.

La aplicación tiene una serie de requisitos, pero uno de esos es que cuando alguien cree una cuenta nueva, la aplicación revisa si la persona es mayor de 18 años. Si la persona tiene 18 años, o más, la cuenta puede ser creada. Si la persona tiene 17 años, o menos, la cuenta no puede ser creada. Super simple, pero es un requisito legal que es muy importante para tu empresa.

Como la aplicación es pequeña, y tu eres el único desarrollador, tú sabes de que en alguna parte de tu código, existe una cosa que revisa la edad. Digamos que la edad es verificada con algo así:

public boolean puedeCrearCuenta(Usuario usuario) {
if (usuario.edad < 17) {
return true;
}
return false;
}


La forma específica de cómo se revisa la edad es irrelevante. Lo que nos importa es que si pasamos un objeto de tipo Usuario, podemos ver si ese usuario puede crear una cuenta.

La aplicación es lanzada al público, y todo sale bien. Tu sabes que esa parte del código es importante, pero es una verificación tan pequeña que no es documentada ni probada.

Tu aplicación - 6 meses después

Tu aplicación está siendo utilizada por muchos usuarios, y el banco le quiere agregar muchas otras habilidades. Lamentablemente tu no puedes hacer todo, por lo que el banco contrata 2 desarrolladores más, o contrata con otra empresa para que ellos puedan crear las habilidades nuevas de la aplicación.

Tu verificacion de edad, que escribiste hace 6 meses, continúa en el código, pero como nunca fue documentada, y está escrita en una forma muy básica, los desarrolladores nuevos modifican el código.

Como los desarrolladores nuevos ven el nombre de “puedeCrearCuenta” piensan de que esta verificación puede tener muchas opciones...por lo que ellos le agregan mas cosas.

public boolean puedeCrearCuenta(Usuario usuario) {
if (usuario.edad < 17 && usuario.sueldo > 100000) {
return true;
}
return false;
}


Ahora tu verificacion de edad no solo revisa la edad, sino que también revisa el sueldo.

La aplicación compila, y nadie se da cuenta de la diferencia...hasta que tienes usuarios quejándose.

Unit Test al rescate

Si hubiéramos usado unit test, tal vez hubiéramos creado una par de pruebas como éstas:

public void testCrearCuentaAdulto() {
Usuario usuario = new Usuario();
usuario.nombre = "Eduardo Flores";
usuario.edad = 18;
assertTrue(puedeCrearCuenta(usuario));
}


public void testCrearCuentaMenorDeEdad() {
Usuario usuario = new Usuario();
usuario.nombre = "Eduardo Flores";
usuario.edad = 17;
assertFalse(puedeCrearCuenta(usuario));
}


Estas 2 pruebas son super simples. Lo único que estamos haciendo es crear un nuevo objeto de tipo Usuario, le damos un nombre al usuario y le damos una edad.

La primera prueba válida a el usuario de 18 años, y por lo tanto puede crear una cuenta. La segunda prueba revisa de que un usuario de 17 años no pueda crear una cuenta.

Ambas pruebas deberían pasar, sin importar el sueldo.

Qué logramos con nuestro Unit Test?

Es entendible de que en este momento tu digas “es obvio de que ambas pruebas van a pasar. Yo escribí el código, y yo escribí las pruebas. Y ambas pruebas son extremadamente simples!”
Y tendrías toda la razón, y es muy bueno que pase las pruebas.

La verdad es que Unit Testing no tiene nada de magia ni nada específico de código que nos importe. Comúnmente las pruebas de Unit Test son así de simples: creas un objeto, y pruebas un elemento específico de tu código.

En donde Unit Test es increíblemente poderoso es en el proceso de cómo desarrollamos la aplicación.

Es decir, Unit Testing es 90% proceso y 10% código...y el que escribas es comúnmente irrelevante.

Si tu aplicación hubiera tenido unit tests, como las 2 propuestas en este post, cuando los desarrolladores nuevos modifiquen el código del método puedeCrearCuenta(), una de las 2 pruebas va a fallar porque no estamos pasando un sueldo. Esto hubiera sido una prueba muy simple y rápida para ver que el código agregado a puedeCrearCuenta() no deberia ir ahi, o simplemente no funciona.

Como crear el proceso

Depende de tu empresa, existen 2 formas en cómo verificar tu código con tus pruebas de unit test:
  1. Puedes hacer correr tu código de unit test arbitrariamente. Un ejemplo de esto es hacer correr las pruebas de unit test cada cierta cantidad de días, o antes de lanzar una actualización (o aplicación nueva)
  2. Puedes usar una aplicación a nivel de creación de tu build para correr tus pruebas de unit test. Nosotros usamos Jenkins, y cada vez que alguien escribe código nuevo a el repositorio, jenkins corre la aplicación y las pruebas de unit test. Si alguna prueba falla, le manda un email a todo el equipo.
El único cuasi-requisito de unit test es de que prueben 1 sola cosa específica. No te sirve de nada si la prueba de unit test revisa la edad, un nombre valido, una dirección correcta, etc… ya que si la prueba falla, el reporte enviado a todos es muy vago.

Conclusion

Espero de que este post te sirva para entender de que Unit Test no es requerido, pero es parte de un proceso que te puede ayudar a eliminar errores lógicos.
Unit testing tambien sirve mucho para verificar de que las cosas que funcionaron hace un año sigan funcionando hoy.

Te entendería de que pensaras de que puedes evitar el uso de Unit Test si usas mejor nombre para tu código, o comentarios, o documentación, pero lamentablemente eso comúnmente no sirve a largo plazo. Piensa de que si tus comentarios son en español, y tu empresa contrata con una empresa Alemana o Rusa o Inglesa, los contratistas no van a entender nada de tu documentación.

Aun así, si tu documentación fuera válida, no dice nada de cómo implementar el método. Que pasaria si en vez de revisar un integer (17) revisaramos en contra de Date() o en milliseconds? Es posible de que cambiemos la implementación, y tengamos un error lógico de cual es el límite de edad con la nueva implementación.

Eduardo.

Wednesday, December 2, 2015

Notificaciones en Android usando Parse.com

El proposito de este tutorial
El producto final va a ser una simple notificacion enviada desde la pagina web de Parse.com, y usaremos la plataforma de Parse para recibirla en Android. Cuando el usuario toque la notificacion en Android, la applicacion de Android se abrira automaticamente.
Todo esto se hara al configurar la plataforma de Parse.com y su SDK de Android.


Si prefieres ver este tutorial en video, subi un video mostrando esto pasa a paso en este link.


Que se necesita para este tutorial


Una cuenta en Parse.com. Una cuenta gratis sera suficiente para este ejercicio.
Un proyecto en Parse. Cualquier proyecto en Parse.com servira. (Si esto es muy complicado mandame un email y agrego los pasos para hacer el proyecto)
Android Studio
Un telefono Android, o el emulador

Comencemos!

Este tutorial esta hecho usando la guia de Parse.com para un proyecto ya existente de Android en vez de un proyecto nuevo. De esta forma este tutorial servira para ambos casos.

1. Obten la informacion de tu proyecto en Parse
Una vez que creas el proyecto en Parse, deberias ver algo similar a esto (Nov. 2015):


Primero que nada vamos a usar Gradle para instalar las librerias, por lo que no no va a ser necesario descargar el archivo .zip. No te preocupes de esto e ignora el primer paso mencionado en Parse.
Sin embargo, ya que vamos a usar Gradle para crear el proyector, es necesario agregar las dependencias que se muestran en el segundo paso. Haremos esto mas adelante.

La siguiente seccion es tambien muy importante ya que tiene nuestro ID y llave del proyecto de parse (Parse Application Id y Client Key).
Tu deberias tener tu propio par de ID y llave para usar especificamente con tu proyecto (Yo escondi los mios aca):

Por ahora deja abierta esta pagina web con la informacion de tu proyecto de Parse. Vamos a usar esta information en el proyecto de Android.

2. Crea un proyecto en Androi, o abre un proyecto existente
Si vas a crear un proyecto nuevo, cualquier proyecto va a servir para esta prueba. Es mejor si que tu proyecto soporte Android OS 4.1, o mas nuevo.

Para este tutorial yo cree un nuevo proyecto llamado "Test_ParsePush", y el nombre de el paquete de este proyecto es "com.eduardoflores.test_parsepush".
No importa cual es el nombre de tu paquete, pero si es necesario acordarse cual es ya que lo usaremos en el archivo de manifest.

3. Modifica tu archivo build.gradle (Module:app)
En este momento la estructura de tu proyecto en Android Studio deberia ser similar a esta:

Abre el archivo build.gradle que esta en tu modulo (si solo tienes 1 modulo, el modulo se llamara "app").
Al final de este archivo, en la seccion de "dependencies" vamos a agregar las dependencias de Parse que te dio Parse en su website al comienzo. Esto agregara el SDK de Parse a tu proyecto:
    compile 'com.parse.bolts:bolts-android:1.+'
    compile 'com.parse:parse-android:1.+'
Tus dependencias deberian ser ahora similares a estas (tu talvez tengas un poco mas o menos dependencias que venian con tu proyecto):
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.0.1'
    compile 'com.android.support:design:23.0.1'
    compile 'com.parse.bolts:bolts-android:1.+'
    compile 'com.parse:parse-android:1.+'
}

Ahora agrega el repositorio maven de donde vas a sacar las herramientas de Parse. (nota: esto es algo que no se menciona en los tutoriales de Parse):
buildscript {
    repositories {
        mavenCentral()
        maven { url 'https://maven.parse.com/repo' }
    }
    dependencies {
        classpath 'com.parse.tools:gradle:1.+'
    }
}
Y ahora finalmente agrega el plugin de Parse al comienzo de tu archivo build.gradle, asi:
apply plugin: 'com.parse'
Ahora tu archivo build.gradle completo deberia se deberia ver asi:
apply plugin: 'com.android.application'
apply plugin: 'com.parse'

buildscript {
    repositories {
        mavenCentral()
        maven { url 'https://maven.parse.com/repo' }
    }
    dependencies {
        classpath 'com.parse.tools:gradle:1.+'
    }
}

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.1"

    defaultConfig {
        applicationId "eduardoflores.com.test_parsepush"
        minSdkVersion 16
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.0.1'
    compile 'com.android.support:design:23.0.1'
    compile 'com.parse.bolts:bolts-android:1.+'
    compile 'com.parse:parse-android:1.+'
}
 
Estamos listos con el archivo build.gradle y lo puedes cerrar.

4. Modifica tu archivo Manifest
Abre el archivo de manifest en tu aplicacion, en Android.
- En el archivo manifest, antes de <application, necesitas agregar 2 cosas.
La primera cosa es esta:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
La segunda cosa require cambiar el nombre del paquete a el nombre del paquete de tu aplicacion de Android:
<permission android:protectionLevel="signature"
                android:name="TU_PAQUETE.permission.C2D_MESSAGE" />
<uses-permission android:name="TU_PAQUETE.permission.C2D_MESSAGE" />
Aca, es importante de que cambies el texto de "TU_PAQUETE" con el nombre del paquete de tu aplicacion de Android. En mi caso, como lo mencione en el paso numero 2, el nombre del paquete de mi aplicacion es "com.eduardoflores.test_parsepush"

- Ahora en el mismo archivo de manifest, entre las ultimas etiquetas </activity> y </application>, necesitas poner la informacion de GCM para recibir tu mensaje:
<service android:name="com.parse.PushService" />
<receiver android:name="com.parse.ParsePushBroadcastReceiver"
          android:exported="false">
    <intent-filter>
        <action android:name="com.parse.push.intent.RECEIVE" />
        <action android:name="com.parse.push.intent.DELETE" />
        <action android:name="com.parse.push.intent.OPEN" />
    </intent-filter>
</receiver>
<receiver android:name="com.parse.GcmBroadcastReceiver"
          android:permission="com.google.android.c2dm.permission.SEND">
    <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
        <category android:name="TU_PAQUETE" />
    </intent-filter>
</receiver>
Date cuenta de que tambien debes poner el nombre del paquete de tu aplicacion en la ultima categoria de nombre.
Y asi, con el nombre del paquete de la aplicacion reemplazado, todo tu archivo manifest deberia ser similar a esto:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="eduardoflores.com.test_parsepush" >

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
    <permission android:protectionLevel="signature"
                android:name="eduardoflores.com.test_parsepush.permission.C2D_MESSAGE" />
    <uses-permission android:name="eduardoflores.com.test_parsepush.permission.C2D_MESSAGE" />

    <application
        android:name=".StarterClass"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service android:name="com.parse.PushService" />
        <receiver android:name="com.parse.ParsePushBroadcastReceiver"
                  android:exported="false">
            <intent-filter>
                <action android:name="com.parse.push.intent.RECEIVE" />
                <action android:name="com.parse.push.intent.DELETE" />
                <action android:name="com.parse.push.intent.OPEN" />
            </intent-filter>
        </receiver>
        <receiver android:name="com.parse.GcmBroadcastReceiver"
                  android:permission="com.google.android.c2dm.permission.SEND">
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
                <category android:name="eduardoflores.com.test_parsepush" />
            </intent-filter>
        </receiver>
    </application>
</manifest>
Y ahora estas tambien listo con el archivo de manifest (lo de name=.StarterClass lo voy a explicar despues).

5. Escribe el codigo de Java en tu aplicacion Android
En tu actividad inicial debes inicializar el SDK de Parse con las llaves de Parse y el ID, y luego le dices a Parse que guarde eso en otro proceso.

Lo siguiente es muy importante: TU APLICACION NO VA A FUNCIONAR SI INICIALIZAS PARSE MAS DE UNA VEZ DENTRO DE TU APLICACION.

Que significa esto? Si tu inicializas Parse en el metodo de tu MainActivity, la aplicacion va a funcionar y vas a poder recibir notificaciones, pero solo si la aplicacion esta corriendo.
Una vez que la notificacion llegue tu aplicacion va a comenzar, el metodo onCreate() va a ser llamado nuevamente y Parse va a ser inicializado de nuevo. Esto hara que la aplicacion se termine con un mensaje de "Unable to create service com.parse.PushService: java.lang.NullPointerException" (no es posible crear un servicio de com.parse.PushService)
Para evitar esto vamos a crear una actividad simple que lo unico que va a ser es comenzar la aplicacion, junto con inicializar la libreria de Parse.


Crea una clase simple

Crea un nuevo archivo de java llamado "StarterClass.java" y haz que esta clase extienda Activity.

En la clase StarterClass, crea un metodo onCreate(), y adentro de este metodo, despues del super() metodo, agrega esto:
Parse.initialize(this, APPLICATION_ID, CLIENT_KEY);
ParseInstallation.getCurrentInstallation().saveInBackground();

Asi con eso, tu clase basica completa va a ser asi:
package eduardoflores.com.test_parsepush;

import com.parse.Parse;
import com.parse.ParseInstallation;

import android.app.Application;

/**
 * @author Eduardo Flores
 */
public class StarterClass extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        // Inicializa Parse
        Parse.initialize(this, "application_id_from_parse", "client_key_from_parse");
        ParseInstallation.getCurrentInstallation().saveInBackground();
    }
}
 
Esta clase StarterClass que creamos es lo que definimos en el archivo manifest bajo el nombre de name=".StarterClass" para la aplicacion. Asi esta clase es invocada solo una vez, haciendo que Parse tambien se inicialize una sola vez.

Y con eso, todo lo que necesitas ahora es mandar notificaciones desde Parse.com!
Corre tu aplicacion de Android y manda un mensaje desde Parse.com, y todo deberia funcionar como esto:


 

Y finalmente, puedes bajar todo el codigo de este tutorial desde aca.


Eduardo Flores.