<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Mundo Geek &#187; pruebas</title>
	<atom:link href="http://mundogeek.net/etiqueta/pruebas/feed/" rel="self" type="application/rss+xml" />
	<link>http://mundogeek.net</link>
	<description>Mundo geek, bitácora sobre todo lo geek: software, gadgets, tecnología, internet, ...</description>
	<lastBuildDate>Wed, 08 Feb 2012 16:09:25 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Pruebas en Python</title>
		<link>http://mundogeek.net/archivos/2008/09/17/pruebas-en-python/</link>
		<comments>http://mundogeek.net/archivos/2008/09/17/pruebas-en-python/#comments</comments>
		<pubDate>Wed, 17 Sep 2008 10:33:59 +0000</pubDate>
		<dc:creator>Zootropo</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[doctest]]></category>
		<category><![CDATA[pruebas]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[tutorial]]></category>
		<category><![CDATA[unittest]]></category>

		<guid isPermaLink="false">http://mundogeek.net/?p=1744</guid>
		<description><![CDATA[Para asegurar en la medida de lo posible el correcto funcionamiento y la calidad del software se suelen utilizar distintos tipos de pruebas, como pueden ser las pruebas unitarias, las pruebas de integración, o las pruebas de regresión. A lo largo de este capítulo nos centraremos en las pruebas unitarias, mediante las que se comprueba [...]]]></description>
			<content:encoded><![CDATA[<p>Para asegurar en la medida de lo posible el correcto funcionamiento y la calidad del software se suelen utilizar distintos tipos de pruebas, como pueden ser las pruebas unitarias, las pruebas de integración, o las pruebas de regresión.</p>
<p>A lo largo de este capítulo nos centraremos en las pruebas unitarias, mediante las que se comprueba el correcto funcionamiento de las unidades lógicas en las que se divide el programa, sin tener en cuenta la interrelación con otras unidades.<br />
<span id="more-1744"></span><br />
La solución más extendida para las pruebas unitarias en el mundo Python es <code>unittest</code>, a menudo combinado con <code>doctest</code> para pruebas más sencillas. Ambos módulos están incluídos en la librería estándar de Python.</p>
<h2>Doctest</h2>
<p>Como es de suponer por el nombre del módulo, <code>doctest</code> permite combinar las pruebas con la documentación. Esta idea de utilizar las pruebas unitarias para probar el código y también a modo de documentación permite realizar pruebas de forma muy sencilla, propicia el que las pruebas se mantengan actualizadas, y sirve a modo de ejemplo de uso del código y como ayuda para entender su propósito.</p>
<p>Cuando <code>doctest</code> encuentra una línea en la documentación que comienza con &#8216;<code>&gt;&gt;&gt;</code>&#8216; se asume que lo que le sigue es código Python a ejecutar, y que la respuesta esperada se encuentra en la línea o líneas siguientes, sin <code>&gt;&gt;&gt;</code>. El texto de la prueba termina cuando se encuentra una línea en blanco, o cuando se llega al final de la cadena de documentación.</p>
<p>Tomemos como ejemplo la siguiente función, que devuelve una lista con los cuadrados de todos los números que componen la lista pasada como parámetro:</p>
<pre name="code" class="python">def cuadrados(lista):
    """Calcula el cuadrado de los numeros de una lista"""

    return [n ** 2 for n in lista]</pre>
<p>Podríamos crear una prueba como la siguiente, en la que comprobamos que el resultado al pasar la lista <code>[0, 1, 2, 3]</code> es el que esperábamos:</p>
<pre name="code" class="python">def cuadrados(lista):
    """Calcula el cuadrado de los numeros de una lista

    >>> l = [0, 1, 2, 3]
    >>> cuadrados(l)
    [0, 1, 4, 9]
    """

    return [n ** 2 for n in lista]</pre>
<p>Lo que hacemos en este ejemplo es indicar a <code>doctest</code> que cree un lista <code>l</code> con valor <code>[0, 1, 2, 3]</code>, que llame a continuación a la función <code>cuadrados</code> con <code>l</code> como argumento, y que compruebe que el resultado devuelto sea igual a <code>[0, 1, 4, 9]</code>.</p>
<p>Para ejecutar las pruebas se utiliza la función <code>testmod</code> del módulo, a la que se le puede pasar opcionalmente el nombre de un módulo a evaluar (parámetro <code>name</code>). En el caso de que no se indique ningún argumento, como en este caso, se evalúa el módulo actual:</p>
<pre name="code" class="python">def cuadrados(lista):
    """Calcula el cuadrado de los numeros de una lista

    >>> l = [0, 1, 2, 3]
    >>> cuadrados(l)
    [0, 1, 4, 9]
    """

    return [n ** 2 for n in lista]

def _test():
    import doctest
    doctest.testmod()

if __name__ == "__main__":
    _test()</pre>
<p>En el caso de que el código no pase alguna de las pruebas que hemos definido, <code>doctest</code> mostrará el resultado obtenido y el resultado esperado. En caso contrario, si todo es correcto, no se mostrará ningún mensaje, a menos que añadamos la opción <code>-v</code> al llamar al script o el parámetro <code>verbose=True</code> a la función <code>tesmod</code>, en cuyo caso se mostrarán todas las pruebas ejecutadas, independientemente de si se ejecutaron con éxito.</p>
<p>Este sería el aspecto de la salida de <code>doctest</code> utilizando el parámetro <code>-v</code>:</p>
<pre><code>Trying:
    l = [0, 1, 2, 3]
Expecting nothing
ok
Trying:
    cuadrados(l)
Expecting:
    [0, 1, 4, 9]
ok
2 items had no tests:
    __main__
    __main__._test
1 items passed all tests:
   2 tests in __main__.cuadrados
2 tests in 3 items.
2 passed and 0 failed.
Test passed.</code></pre>
<p>Ahora vamos a introducir un error en el código de la función para ver el aspecto de un mensaje de error de <code>doctest</code>. Supongamos, por ejemplo, que hubieramos escrito un operador de multiplicación (&#8216;*&#8217;) en lugar de uno de exponenciación (&#8216;**&#8217;):</p>
<pre name="code" class="python">def cuadrados(lista):
    """Calcula el cuadrado de los numeros de una lista

    >>> l = [0, 1, 2, 3]
    >>> cuadrados(l)
    [0, 1, 4, 9]
    """

    return [n * 2 for n in lista]

def _test():
    import doctest
    doctest.testmod()

if __name__ == "__main__":
    _test()</pre>
<p>Obtendríamos algo parecido a esto:</p>
<pre><code>**********************************************************************
File "ejemplo.py", line 5, in __main__.cuadrados
Failed example:
    cuadrados(l)
Expected:
    [0, 1, 4, 9]
Got:
    [0, 2, 4, 6]
**********************************************************************
1 items had failures:
   1 of   2 in __main__.cuadrados
***Test Failed*** 1 failures.</code></pre>
<p>Como vemos, el mensaje nos indica que ha fallado la prueba de la línea 5, al llamar a <code>cuadrados(l)</code>, cuyo resultado debería ser <code>[0, 1, 4, 9]</code>, y sin embargo obtuvimos <code>[0, 2, 4, 6]</code>.</p>
<p>Veamos por último cómo utilizar sentencias anidadas para hacer cosas un poco más complicadas con <code>doctest</code>. En el ejemplo siguiente nuestra función calcula el cuadrado de un único número pasado como parámetro, y diseñamos una prueba que compruebe que el resultado es el adecuado para varias llamadas con distintos valores. Las sentencias anidadas comienzan con &#8220;<code>...</code>&#8221; en lugar de &#8220;<code>&gt;&gt;&gt;</code>&#8220;:</p>
<pre name="code" class="python">def cuadrado(num):
    """Calcula el cuadrado de un numero.

    >>> l = [0, 1, 2, 3]
    >>> for n in l:
    ...     cuadrado(n)
    [0, 1, 4, 9]
    """

    return num ** 2

def _test():
    import doctest
    doctest.testmod()

if __name__ == "__main__":
    _test()</pre>
<h2>unittest / PyUnit</h2>
<p><code>unittest</code>, también llamado PyUnit, forma parte de una familia de herramientas conocida colectivamente como xUnit, un conjunto de frameworks basados en el software SUnit para Smalltalk, creado por Kent Beck, uno de los padres de la eXtreme Programming. Otros ejemplos de herramientas que forman parte de esta familia son JUnit para Java, creada por el propio Kent Beck junto a Erich Gamma, o NUnit, para .NET.</p>
<p>El uso de <code>unittest</code> es muy sencillo. Para cada grupo de pruebas tenemos que crear una clase que herede de <code>unittest.TestCase</code>, y añadir una serie de métodos que comiencen con <code>test</code>, que serán cada una de las pruebas que queremos ejecutar dentro de esa batería de pruebas.</p>
<p>Para ejecutar las pruebas, basta llamar a la función <code>main()</code> del módulo, con lo que se ejecutarán todos los métodos cuyo nombre comience con <code>test</code>, en orden alfanumérico. Al ejecutar cada una de las pruebas el resultado puede ser:</p>
<ul>
<li>OK: La prueba ha pasado con éxito.</li>
<li>FAIL: La prueba no ha pasado con éxito. Se lanza una excepción <code>AssertionError</code> para indicarlo.</li>
<li>ERROR: Al ejecutar la prueba se lanzó una excepción distinta de <code>AssertionError</code></li>
</ul>
<p>En el siguiente ejemplo, dado que el método que modela nuestra prueba no lanza ninguna excepción, la prueba pasaría con éxito.</p>
<pre name="code" class="python">import unittest

class EjemploPruebas(unittest.TestCase):
    def test(self):
        pass

if __name__ == "__main__":
    unittest.main()</pre>
<p>En este otro, sin embargo, fallaría:</p>
<pre name="code" class="python">import unittest

class EjemploPruebas(unittest.TestCase):
    def test(self):
        raise AssertionError()

if __name__ == "__main__":
    unittest.main()</pre>
<p>Nada nos impide utilizar cláusulas <code>if</code> para evaluar las condiciones que nos interesen y lanzar una excepción de tipo <code>AssertionError</code> cuando no sea así, pero la clase <code>TestCase</code> cuenta con varios métodos que nos pueden facilitar la tarea de realizar comprobaciones sencillas. Son los siguientes:</p>
<ul>
<li><code>assertAlmostEqual(first, second, places=7, msg=None)</code>: Comprueba que los objetos pasados como parámetros sean iguales hasta el séptimo decimal (o el número de decimales indicado por <code>places</code>).</li>
<li><code>assertEqual(first, second, msg=None)</code>: Comprueba que los objetos pasados como parámetros sean iguales.</li>
<li><code>assertFalse(expr, msg=None)</code>: Comprueba que la expresión sea falsa.</li>
<li><code>assertNotAlmostEqual(first, second, places=7, msg=None)</code>: Comprueba que los objetos pasados como parámetros no sean iguales hasta el séptimo decimal (o hasta el número de decimales indicado por <code>places</code>).</li>
<li><code>assertNotEqual(first, second, msg=None)</code>: Comprueba que los objetos pasados como parámetros no sean iguales.</li>
<li><code>assertRaises(excClass, callableObj, *args, **kwargs)</code>: Comprueba que al llamar al objeto <code>callableObj</code> con los parámetros definidos por <code>*args</code> y <code>**kwargs</code> se lanza una excepción de tipo <code>excClass</code>.</li>
<li><code>assertTrue(expr, msg=None)</code>: Comprueba que la expresión sea cierta.</li>
<li><code>assert_(expr, msg=None)</code>: Comprueba que la expresión sea cierta.</li>
<li><code>fail(msg=None)</code>: Falla inmediatamente.</li>
<li><code>failIf(expr, msg=None)</code>: Falla si la expresión es cierta.</li>
<li><code>failIfAlmostEqual(first, second, places=7, msg=None)</code>: Falla si los objetos pasados como parámetros son iguales hasta el séptimo decimal (o hasta el número de decimales indicado por <code>places</code>).</li>
<li><code>failIfEqual(first, second, msg=None)</code>: Falla si los objetos pasados como parámetros son iguales.</li>
<li><code>failUnless(expr, msg=None)</code>: Falla a menos que la expresión sea cierta.</li>
<li><code>failUnlessAlmostEqual(first, second, places=7, msg=None)</code>: Falla a menos que los objetos pasados como parámetros sean iguales hasta el séptimo decimal (o hasta el número de decimales indicado por <code>places</code>).</li>
<li><code>failUnlessEqual(first, second, msg=None)</code>: Falla a menos que los objetos pasados como parámetros sean iguales.</li>
<li><code>failUnlessRaises(excClass, callableObj, *args, **kwargs)</code>: Falla a menos que al llamar al objeto <code>callableObj</code> con los parámetros definidos por <code>*args</code> y <code>**kwargs</code> se lance una excepción de tipo <code>excClass</code>.</li>
</ul>
<p>Como vemos todos los métodos cuentan con un parámetro opcional <code>msg</code> con un mensaje a mostrar cuando dicha comprobación falle.</p>
<p>Retomemos nuestra pequeña función para calcular el cuadrado de un número. Para probar el funcionamiento de la función podríamos hacer, por ejemplo, algo así:</p>
<pre name="code" class="python">import unittest

def cuadrado(num):
    """Calcula el cuadrado de un numero."""

    return num ** 2

class EjemploPruebas(unittest.TestCase):
    def test(self):
        l = [0, 1, 2, 3]
        r = [cuadrado(n) for n in l]
        self.assertEqual(r, [0, 1, 4, 9])

if __name__ == "__main__":
    unittest.main()</pre>
<h3>Preparación del contexto</h3>
<p>En ocasiones es necesario preparar el entorno en el que queremos que se ejecuten las pruebas. Por ejemplo, puede ser necesario introducir unos valores por defecto en una base de datos, crear una conexión con una máquina, crear algún archivo, etc. Esto es lo que se conoce en el mundo de xUnit como <em>test fixture</em>.</p>
<p>La clase <code>TestCase</code> proporciona un par de métodos que podemos sobreescribir para construir y desconstruir el entorno y que se ejecutan antes y después de las pruebas definidas en esa clase. Estos métodos son <code>setUp()</code> y <code>tearDown()</code>.</p>
<pre name="code" class="python">class EjemploFixture(unittest.TestCase):
    def setUp(self):
        print "Preparando contexto"
        self.lista = [0, 1, 2, 3]

    def test(self):
        print "Ejecutando prueba"
        r = [cuadrado(n) for n in self.lista]
        self.assertEqual(r, [0, 1, 4, 9])

    def tearDown(self):
        print "Desconstruyendo contexto"
        del self.lista</pre>
<link type="text/css" rel="stylesheet" href="http://mundogeek.net/sh/css/SyntaxHighlighter.css"></link>
<script language="javascript" src="http://mundogeek.net/sh/js/shCore.js"></script><br />
<script language="javascript" src="http://mundogeek.net/sh/js/shBrushPython.js"></script><br />
<script language="javascript">dp.SyntaxHighlighter.ClipboardSwf = 'http://mundogeek.net/sh//flash/clipboard.swf';
dp.SyntaxHighlighter.HighlightAll('code');</script></p>
]]></content:encoded>
			<wfw:commentRss>http://mundogeek.net/archivos/2008/09/17/pruebas-en-python/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>El código y las pruebas</title>
		<link>http://mundogeek.net/archivos/2007/10/24/el-codigo-y-las-pruebas/</link>
		<comments>http://mundogeek.net/archivos/2007/10/24/el-codigo-y-las-pruebas/#comments</comments>
		<pubDate>Wed, 24 Oct 2007 17:13:49 +0000</pubDate>
		<dc:creator>Zootropo</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[citas]]></category>
		<category><![CDATA[codigo]]></category>
		<category><![CDATA[programacion]]></category>
		<category><![CDATA[pruebas]]></category>
		<category><![CDATA[tests]]></category>

		<guid isPermaLink="false">http://mundogeek.net/archivos/2007/10/24/el-codigo-y-las-pruebas/</guid>
		<description><![CDATA[Escribir código sin pruebas automátizadas es como intentar usar ladrillos sin cemento. &#8211; Anónimo]]></description>
			<content:encoded><![CDATA[<blockquote><p>Escribir código sin pruebas automátizadas es como intentar usar ladrillos sin cemento.</p>
<p>&#8211; Anónimo</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://mundogeek.net/archivos/2007/10/24/el-codigo-y-las-pruebas/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>

