Technical stuff

Technology, computers, and the likes.

Cómo elegir tests de unidad

En mi artículo anterior os expliqué cómo escribir tests de unidad, pero pasé prácticamente por alto un tema muy importante: ¿qué tests de unidad escribimos? Por desgracia, el párrafo que le dediqué lo explicaba tan mal y de forma tan resumida, que mucha gente se llevó la idea equivocada, así que para solucionarlo aquí va un nuevo artículo dedicado exclusivamente a ese tema.


Por lo general, nuestro objetivo cuando escribimos tests de unidad es comprobar que esta unidad funciona correctamente en los casos normales. Fijaos en que he dicho “comprobar” y no “demostrar”: no queremos una demostración de que la unidad siempre produce una salida correcta con todas las posibles combinaciones de datos de entrada, sino una manera automatizada de comprobar que la implementación funciona bien.

Parece la misma cosa, pero no lo es, y la distinción es importante. Si quisiéramos demostrar que la implementación de un módulo de gestión de clientes es correcta, tendríamos que hacer un catálogo exhaustivo de posibles entradas correctas y de posibles entradas incorrectas, pasarlas todas por el módulo, y comprobar que producen las salidas esperadas. Esto puede suponer mucho tiempo y esfuerzo. Sin embargo, para comprobar que el módulo funciona, no tenemos que matarnos tanto, ya que las pruebas son más informales: grabar un registro, comprobar que se puede leer, modificarlo, comprobar que ahora se lee la versión modificada, borrarlo, ver qué pasa si se intenta obtener un registro que no existe, etc. Mientras que tenemos que asegurarnos de que probamos toda la funcionalidad, estas pruebas no necesitan ser totalmente exhaustivas. De este modo, evitamos que la creación y mantenimiento de los tests de unidad supongan una carga excesiva.

El nivel de detalle al que deberíamos llegar depende del tipo de unidad que queremos probar. Por ejemplo, para una biblioteca que va a ser utilizada por programadores externos, los tests son muy detallados, comprobando diversas entradas válidas y no válidas, etc. Sin embargo, para un módulo interno de una aplicación, los tests sólo cubrirán por lo general los casos que se encuentren en el funcionamiento normal del programa, sin pararse a considerar casos triviales o irrelevantes. En algunos casos no vale la pena escribir tests de unidad; por ejemplo, un “value object” que consista exclusivamente en funciones “get” y “set” triviales no necesita uno.

Los tests de Apache Harmony (una reimplementación libre de las bibliotecas estándar de Java) son un buen ejemplo del primer caso. Por ejemplo, echadle un ojo a ArrayListTest, y veréis que cada test comprueba un método de ArrayList diferente, llamándolo de diferentes maneras, y comprobando que producen los resultados esperados de acuerdo con la especificación.

Para un ejemplo del segundo caso, mirad en Apache Shindig (implementación de referencia de OpenSocial) la clase BasicOAuthStoreTest, que usa unos cuantos datos de ejemplo representativos de un funcionamiento normal del programa para comprobar que las funciones de BasicOAuthStore hacen lo que se supone que deben hacer, pero sin pretender que estas pruebas sean exhaustivas.

Espero que este artículo os haya despejado unas cuantas dudas, y nos veremos en la próxima entrega de la serie :)

(Primer artículo, siguiente artículo).

Cómo escribir tests de unidad

En mi anterior artículo os expliqué varias razones por las que los tests de unidad son importantes, pero no tuve espacio para explicaros cómo crearlos, así que he escrito éste para solucionar ese problema.


Los tests de unidad son programitas que comprueban que una “unidad” funciona correctamente. Una unidad es un grupo de funcionalidad coherente y autocontenido; esto, normalmente, significa “una clase del programa”.

Para escribir los tests de unidad, lo primero que debemos hacer es preguntarnos: “¿cuáles son las propiedades que ha de cumplir esta unidad en todo momento? ¿qué casos he de comprobar, porque los errores suelen ocultarse en ellos?”. Cuando tengamos la respuesta, sólo tendremos que escribir código que compruebe que esas propiedades realmente se cumplen, y que esos casos no producen errores. Tened en cuenta que los tests no tienen que ser exhaustivos, o nos supondrán una carga de trabajo excesiva; he escrito un artículo sobre el tema.

Para escribir tests de unidad se suelen utilizar “frameworks” que proporcionan todo el andamiaje y funciones auxiliares necesarios. Existen muchísimos frameworks libres para casi todos los lenguajes de programación: JUnit (para Java), PyUnit (para Python), JsUnit (para JavaScript), CppUnit (para C++), etc. Como los ejemplos de este artículo están escritos en Java, he decidido emplear JUnit 4.

En el mundo Java existe la convención de poner el código fuente del programa en un directorio llamado “src” y el código fuente de los tests en un directorio llamado “tests”. Además, se configura el sistema de compilación para que los ficheros .class generados al compilar los tests vayan en un directorio especial. De este modo, no se mezclan el código de producción y los tests. En Python y C++ las convenciones son diferentes, pero el objetivo es el mismo: que no se mezcle el código de producción y el código de test.

Dicho esto, vamos a ver el código de la clase para la que quiero escribir los tests de unidad. “MyList” es una implementación de una lista usando un array. Si queréis probar esto en vuestro ordenador y estáis usando Eclipse, cread un proyecto nuevo y poned el código de la clase dentro del directorio “src”.

package org.tarrio.tutorial.unittests;

public class MyList<E> {

	private static final int SIZE_DELTA = 2;

	private Object[] elems;
	private int size;

	public MyList() {
		elems = new Object[SIZE_DELTA];
		size = 0;
	}
	
	public int size() {
		return size;
	}

	public void add(E o) {
		ensureSize(size + 1);
		elems[size++] = o;
	}

	public E get(int index) {
		checkIndex(index);
		@SuppressWarnings("unchecked")
		E ret = (E) elems[index];
		return ret;
	}

	public void set(int index, E o) {
		checkIndex(index);
		elems[index] = o;
	}

	public void remove(int index) {
		checkIndex(index);
		if (index != size - 1) {
			System.arraycopy(elems, index + 1, elems, index, size - index - 1);
		}
		--size;
	}

	private void ensureSize(int newSize) {
		if (newSize > elems.length) {
			Object[] newElems = new Object[elems.length + SIZE_DELTA];
			System.arraycopy(elems, 0, newElems, 0, size);
			elems = newElems;
		}
	}

	private void checkIndex(int index) {
		if (index < 0 || index >= size) {
			throw new IndexOutOfBoundsException();
		}
	}
}

Una vez hecho esto, vamos a comenzar a escribir el test de unidad. Cread un directorio fuente (“source folder”) llamado “tests” y configuradlo para que las clases compiladas vayan a un directorio distinto de “bin” (en Eclipse, pulsad con el botón derecho sobre “tests” y seleccionad Build Path, Configure Output Folder, Specific output folder, y escribid “tests-bin”).

En este directorio fuente cread un paquete con el mismo nombre que el paquete al que pertenece la clase anterior, y dentro del paquete cread una nueva clase llamada “MyListTest”.

Ahora es cuando debéis preguntaros qué propiedades ha de cumplir MyList, para escribir tests para ellas. Por ejemplo, un MyList recién creado está vacío, al añadir un elemento a MyList el tamaño aumenta en uno, al eliminar un elemento de MyList el tamaño se reduce en uno, los elementos de MyList se pueden recuperar en el mismo orden en el que se introdujeron, etc.

Vamos a escribir un test para la primera propiedad: un MyList recién creado está vacío. Para ello, añadid el siguiente método a vuestra clase MyListTest:

	@Test
	public void newListIsEmpty() {
		MyList<Integer> list = new MyList<Integer>();
		assertEquals(0, list.size());
	}

La anotación @Test (org.junit.Test) indica a JUnit que el siguiente método es un test. El método assertEquals (org.junit.Assert.assertEquals) compara un valor esperado con un valor real, y lanza una excepción si no son iguales. Existen muchos otros métodos assertXxxx que sirven para comprobar si una condición es cierta, si dos arrays son iguales, si un objeto es null, etc.

Para ejecutar esto en Eclipse, pulsad con el botón derecho sobre el nombre de la clase y seleccionad Run As, JUnit test. Si todo va bien, deberíais ver un panel con una barra de progreso y un icono, todos de color verde. Podéis cambiar el 0 por un 1 para ver qué ocurre si la condición del “assert” no se cumple.

Ahora que sabemos cómo escribir un test de unidad, vamos a escribir los tests para “al añadir un elemento el tamaño se incrementa en una unidad” y “al eliminar un elemento el tamaño se reduce en una unidad”:

	@Test
	public void addingOneElementIncreasesSizeByOne() {
		MyList<Integer> list = new MyList<Integer>();
		assertEquals(0, list.size());
		list.add(42);
		assertEquals(1, list.size());
		list.add(592);
		assertEquals(2, list.size());
	}
	
	@Test
	public void removingOneElementIncreasesSizeByOne() {
		MyList<Integer> list = new MyList<Integer>();
		list.add(42);
		list.add(592);
		assertEquals(2, list.size());
		list.remove(0);
		assertEquals(1, list.size());
		list.remove(0);
		assertEquals(0, list.size());
	}

Habréis observado que los tres tests que hemos escrito hasta el momento comienzan de la misma forma, creando una nueva instancia de MyList. Esto es código de inicialización común a todos los tests, y sería conveniente extraerlo a un método de inicialización. JUnit nos permite definir un método que se ejecuta antes de cada test, y en el que podemos realizar todas las tareas de inicialización necesarias, marcándolo con la anotación @Before:

	private MyList<Integer> list;
	
	@Before
	public void setUp() {
		list = new MyList<Integer>();
	}
	
	@Test
	public void newListIsEmpty() {
		assertEquals(0, list.size());
	}
	
	@Test
	public void addingOneElementIncreasesSizeByOne() {
		assertEquals(0, list.size());
		list.add(42);
		assertEquals(1, list.size());
		list.add(592);
		assertEquals(2, list.size());
	}
	
	@Test
	public void removingOneElementIncreasesSizeByOne() {
		list.add(42);
		list.add(592);
		assertEquals(2, list.size());
		list.remove(0);
		assertEquals(1, list.size());
		list.remove(0);
		assertEquals(0, list.size());
	}

Como podéis ver, he eliminado la variable “list” de cada test y he creado un atributo privado “list”, que se inicializa en el método “setUp” que está marcado con @Before. Este método se ejecuta antes de cada test, y gracias a él, el atributo “list” siempre contiene una lista recién creada y vacía.

También existe una anotación @After, que sirve para ejecutar un método después de cada test. Como cada test ha de ser independiente de los demás y no debe tener efectos secundarios duraderos, se pueden utilizar los métodos @After para deshacer cualquier cambio de estado que se haya podido producir en los tests o en el método @Before.

A estas alturas, no os debería ser nada difícil escribir tests para “los elementos que se añaden a la lista se pueden recuperar en el mismo orden”, y para cualquier otra propiedad de MyList que se os ocurra. Este es vuestro primer ejercicio.

No os olvidéis de que tenéis que actualizar los tests de unidad cada vez que modifiquéis MyList. Por ejemplo, si añadís el siguiente método a MyList para añadir todos los elementos de otra lista:

	public void addAll(MyList<E> l) {
		ensureSize(size + l.size());
		System.arraycopy(l.elems, 0, elems, size, l.size);
		size += l.size;
	}

Tendréis que escribir al menos un test que compruebe que funciona bien:

	@Test
	public void addAllIncreasesSizeByOtherListsSize() throws Exception {
		MyList<Integer> someList = new MyList<Integer>();
		someList.add(1);
		someList.add(2);
		someList.add(3);
		list.addAll(someList);
		assertEquals(3, list.size());
		someList.add(4);
		list.addAll(someList);
		assertEquals(7, list.size());
	}

Lo añadimos, ejecutamos los tests, y... ¡hala! ¡El nuevo test falla! ¿Qué ha ocurrido? Resulta que tenemos un error en la clase MyList, y el test que acabamos de añadir lo ha detectado. Vuestro segundo ejercicio consiste en buscar y arreglar el error.

Vuestro tercer ejercicio consiste en cambiar la implementación de MyList, de un array a una lista enlazada. Al final del ejercicio, los mismos tests de unidad deberían seguir pasando sin necesidad de modificarlos.

Espero que este artículo os haya resultado instructivo.

(Primer artículo, siguiente artículo).

Introducción a los tests de unidad

Tengo ganas de “evangelizar” sobre buenas prácticas de desarrollo, así que durante los próximos días publicaré una serie de artículos sobre tests de unidad. Aquí va la introducción; los demás artículos serán más técnicos. Espero que la serie os parezca interesante :)


Los tests de unidad (“unit tests” en inglés) son programitas que ponen a prueba una unidad de un programa. Una unidad puede ser una función, una clase, o incluso un módulo entero. Estos programas ejecutan diversas partes de esta unidad con diversos parámetros de entrada, estados internos, etc., y comprueban que ésta produce los resultados correctos.

Los tests de unidad se deberían escribir junto con el código al que prueban, y se deben mantener actualizados de forma que siempre pasen con éxito (ya sea arreglando los fallos que se puedan introducir en la unidad que se prueba, o actualizando el test si el funcionamiento de la unidad ha cambiado). En algunos sitios incluso escriben los tests antes de escribir el código. Lo que hacen es codificar los requisitos en los tests, y así, cuando todos los tests pasan con éxito, saben que el módulo cumple todos los requisitos y además funciona bien.

Podría parecer a simple vista que mantener los tests de unidad al mismo tiempo que el código “de verdad” cuesta más trabajo que, simplemente, no tener tests de unidad. Sin embargo, los tests de unidad proporcionan varias ventajas que compensan con creces su existencia.

La principal es la mayor velocidad de desarrollo. Cuando hacéis un cambio en un programa y no tenéis tests de unidad, la única forma de probarlo consiste en compilar el programa, ejecutarlo, ir hasta la parte que habéis cambiado, hacer la prueba, etc. Sin embargo, con tests de unidad basta con compilarlos y ejecutarlos, y en menos de un minuto tenéis el resultado.

Además, los tests de unidad evitarán que introduzcáis muchos errores, ya que los tests no pasarán con éxito hasta que la unidad a prueba funcione razonablemente bien. Y, si en el futuro descubrís algún error, sólo tenéis que añadir un test que capture ese error, arreglarlo para que el test pase con éxito, y sabréis que no volveréis a introducir ese error en el futuro.

Otra ventaja es que con los tests de unidad podéis estar seguros de que vuestros cambios no tendrán efectos imprevistos. Por ejemplo, algo que se hace a menudo es cambiar la implementación de un módulo sin modificar su interfaz. Si tenéis tests de unidad e introducís algún error en la nueva implementación, alguno de estos tests fallará; cuando todos los tests pasen con éxito, podéis estar razonablemente seguros de que la nueva implementación es correcta. Sin tests de unidad no podéis estar tan seguros.

Otra ventaja importante es que los tests forman parte de la documentación del software. Los Javadoc tienen la molesta costumbre de quedar obsoletos. Tal vez hoy una función de búsqueda devuelve “null” al buscar un elemento que no existe, pero si mañana alguien la cambia para lanzar una excepción y no actualiza el Javadoc, el compilador no protestará y el Javadoc quedará obsoleto. Sin embargo, si hay un test que comprueba que, al pasarle un elemento inexistente, la función devuelve “null”, cuando esta persona haga el cambio el test fallará, así que tendrá que actualizar el test (o dejar la función como estaba). Por tanto, los tests de unidad son una documentación que nunca queda obsoleta.

Por supuesto, los tests de unidad no sirven de nada si nadie les presta atención. En muchas organizaciones tienen políticas que obligan a ejecutar los tests de unidad antes de hacer “commit” (por supuesto, los tests tienen que pasar con éxito). En algunos sitios tienen “compiladores continuos” (“continuous build”), que son máquinas que toman la última versión del software, lo compilan, ejecutan los tests de unidad y avisan si alguno falla. En otros sitios integran los tests de unidad en el sistema de control de versiones, de forma que no se puede hacer “commit” si algún test falla.

En posteriores historias explicaré cómo escribir tests de unidad, cómo usar inversión de dependencias, mocks y objetos falsos para aislar la unidad que queremos probar, y cómo hacer desarrollo dirigido por los tests.

(Siguiente artículo).

Carteles de Mountain View

¡Por fin! Ya tengo apartamento en Mountain View (bueno, en realidad tengo el recibo de la fianza, pero todo se andará). Hasta ahora estuve en un hotel y, bueno, mola lo de que te limpien la habitación otras personas todos los días, pero al final del mes le duele a uno bastante el bolsillo...

Ayer, cuando venía de que me tomaran por idiota en otro complejo de apartamentos, tomé varias fotos de carteles y señales que me encontré por la calle.

¿Quién adivina en qué calle estoy?
El Camino Real

Todavía andan obsesionados con la gripe A.
H1N1 vaccine

Siempre que pillo el autobús de la empresa veo este cartel que parece anunciar “limpiadores mediums” por 1 dólar y 95 centavos. ¿Por limpiarte la casa de espíritus?
Psychic cleaners
... pero fue ayer cuando descubrí que en realidad es un cartel para dos tiendas distintas: un vidente y una tintorería. Menuda decepción :(

¡Esta tienda lo tiene todo! ¡Vinos, licores y cigarros!
This shop has everything!
Bueno, no todo, pero la tienda de armas de fuego está sólo dos bloques más adelante.

Por último, esto no es un cartel, sino la prueba de que en Mountain View también hay gente de Bilbao :)
Hanging up the phone

My Twitter client for Android, now available

My Twitter client for Android, CheepCheep, is now available on the Android Market. Click the previous link to see the program's webpage with information about its features, screenshots, and how to get it.

Its source code will be published soon under a liberal licence.

Invitacións de Google Wave

Esquecera comentar que aínda teño 5 invitacións de Google Wave. En principio, son para xente que eu coñeza persoalmente, así que se eu te coñezo e queres unha, mándame un email co teu enderezo de GMail, e eu doulle ao botón. (A invitación pode tardar varios días en chegar).

Non valen comentarios nesta páxina; ten que ser un email. Vamos, se me coñeces, sabes como me mandar un :)

Acabaronse!

The Twitter client for Windows^H^H^H^H^H^H^H^H Android

I just wanted to post a note saying that I abandoned the Windows Twitter client a month ago, when I discovered that embedding IE in an application using the raw Win32 API is hard, but not as hard as making it do something more than just browsing web pages (it is very easy with .NET and other frameworks, but, then again, the point was not using them).

But fear not; for the past two weekends I wrote a Twitter client for Android; and, in this case, I will actually use it every day, because I was really fed up with the one I had been using before. I even recycled the name "CheepCheep" for the new client.

Ah, and I programmed it entirely from Windows, of course :)

I will post some more about it one of those days.

CheepCheep

CheepCheep is a Twitter client for Android.

It is not a client for power-users who want to be able to do everything from their phones. The idea is to allow you to read and write tweets and do some simple user management (follow and unfollow users) while you are away from your computer where you can do the rest of the tasks.

Features

  • Supports one Twitter account.
  • Displays 50 tweets in the main screen.
  • Does not display avatar images in tweet lists.
  • Post tweets, reply and ReTweet. Delete your own tweets.
  • Follow links to web page, to Twitter users, and to the original messages in replies.
  • Follow and unfollow Twitter users.
  • Uses OAuth for authentication, so CheepCheep never gets access to your password.
  • No “Settings” screen.

If you think that some of these are limitations, not features, this means that this program was not made for you :)

Download

CheepCheep is in the Android Market; you can get it by searching for CheepCheep in the Market, pressing this link if you are using an Android phone, or simply scanning this QR code:

market://search?q=pname:org.tarrio.cheepcheep

To download the source code, see below.

Screenshots

Move the mouse pointer over the images to see their descriptions.

Loading CheepCheep for the first time User's home timeline Home timeline menu Writing a new Tweet Tweet context menu, after a long press Browsing links in a Tweet, after a short press User's information and timeline User options menu

Source Code

You can browse the source code on GitHub.

You can also download the following versions of the source code:

To compile CheepCheep, extract the appropriate zip file into a suitable location, open Eclipse, then select “File” > “Import...”, then “Existing Projects into Workspace”, and select the location where you extracted the source code.

If you are compiling CheepCheep for the first time, you'll need to register an application on Twitter and alter the source code so it will use your application's OAuth credentials. Read the README.txt file for more information.

You can download and use CheepCheep under the terms of the Expat licence.

Change Log

2014-04-13 Version 0.97

  • Use HTTPS.

2013-03-17 Version 0.96

  • Also access api.twitter.com for OAuth handshake.
  • Fix URL of follow/unfollow methods.

2012-10-16 Version 0.95

  • Fix CheepCheep to access api.twitter.com instead of twitter.com
  • Add positional markers in string resources.

2010-07-31 Version 0.94

  • Allow multi-line in New Tweet input box.
  • Allow to install the application on the SD card in Android 2.2.

2010-03-18 Version 0.931

  • Revert tabbing of user view, as there's a bug that hits me with Android 2.1 (possibly 2.0 too).

2010-03-18 Version 0.93

  • Increase number of tweets shown to 50 in homeline, 40 in user view.
  • Separate user profile and user's tweets into tabs in the user view.

2010-01-25 Version 0.92

  • Get link to original message in retweeted responses.
  • Fixed invalid OAuth signatures when posting updates with non-ASCII chars.

2009-12-15 Version 0.91

  • Parse server-side retweets.
  • Now works in non-English locales (specify US locale when parsing dates).

2009-12-12 Version 0.9

  • Initial release.

State of the art (of the Twitter client)

This is what my Twitter client looks like today.

Current look of CheepCheep

It is displaying my "home timeline"; that is, the list of tweets I see when I log in to Twitter. The timeline is rendered in an embedded Internet Explorer control (and I need to apply some more CSS styles). I have also implemented the preferences dialog, and it works, but I cannot save the preferences yet (I have to enter the credentials every time I restart the application).

Not implemented yet: automatic updates, entering new tweets, deleting your tweets, viewing single tweets/conversations, viewing a single user.

Yes, I'm learning quite a bit writing this program. One of the things I have learnt is that Windows programming is not as horrible as I remember it — but then, it was about 15 years ago :)

More impressions on Windows Programming

I'm still learning how to program for Windows in C++ using the Win32 API, and I'm writing more of my impressions — remember that I've worked with Linux for the past 12 years, and this all is practically new for me.

  • Visual Studio is not a bad IDE, but it is not excellent either (at least for C++). That, or I have been spoilt by Eclipse and IntelliJ IDEA and their excellent support for refactoring (at least for Java). IDEA has such a powerful autocompletion, I have written complete modules without writing a method or variable name in full. In Visual Studio, OTOH, IntelliSense stops working all the time. Are you initializing a variable in the definition? It stops working. Have you got a syntax error 50 lines up? It stops working. Are you using C++ templates? It stops working. I don't know why it stops working so much, when it doesn't even try to figure out whether you want to input a type or a method or a variable name.
  • It is hard work embedding Internet Explorer in an application when you aren't using MFC or Visual Basic or C# or one of those frameworks that give you a "WebBrowser" class in which all the work is already done. Not knowing anything about OLE/COM/ActiveX didn't help, of course. I have to thank Jeff Glatt for his article, “Embed an HTML control in your own window using plain C” (and his “COM in plain C” series), and the authors of wxCode's IEHtmlWin component (once again, Free Software comes to the rescue!). Reading them helped me to understand how it works. Now when I go read the documentation on MSDN, it makes sense!.
  • Talking about MSDN, the quality of the documentation varies. At some points it is very comprehensive and includes tutorials and guides and everything, and can be read as an introductory book. However, at other points it already assumes you know how to do what you want to learn, and only includes a very broad overview and reference information. Not that the documentation in most Linux or free/open source projects is much better in general, mind you...
  • Update: I also want to correct my previous assertion that there was few documentation and my assumption that they kept it scarce on purpose for book publishers' sakes. It turns out there is plenty of documentation (see the point above), only that I didn't know how to find it, as it wasn't in the Express Edition help files. I think. Anyway, it's all online on MSDN.