tdd con python
DESCRIPTION
tdd con python en pdfTRANSCRIPT
![Page 1: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/1.jpg)
Test-Driven Developmentcon Python
y un ejemplo: la librería algoritmia
Andrés MarzalDepartamento de Lenguajes y Sistemas Informáticos
Universitat Jaume [email protected]
viernes 9 de abril de 2010
![Page 2: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/2.jpg)
viernes 9 de abril de 2010
![Page 3: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/3.jpg)
viernes 9 de abril de 2010
![Page 4: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/4.jpg)
”
“If you look at how most programmers spend their time, you’ll find that writing code is actually a small fraction. Some time is spent figuring out what ought to be going on, some time is spent designing, but most time is spent debugging. I’m sure every reader can remember long hours of debugging, often long into the night. Every programmer can tell a story of a bug that took a whole day (or more) to find. Fixing the bug is usually pretty quick, but finding it is a nightmare. And then when you do fix a bug, there’s always a chance that another one will appear and that you might not even notice it until much later. Then you spend ages finding that bug.
Martin Fowler
viernes 9 de abril de 2010
![Page 5: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/5.jpg)
• ¿Qué es TDD?
• ¿Con qué herramientas hacemos TDD con Python?
• Una demo
• unitest
• mockito
• coverage
• Algunas conclusiones
• Algoritmia
Guión
viernes 9 de abril de 2010
![Page 6: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/6.jpg)
¿Qué es TDD?
viernes 9 de abril de 2010
![Page 7: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/7.jpg)
Desarrollo Tradicional
viernes 9 de abril de 2010
![Page 8: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/8.jpg)
Desarrollo TradicionalUnified
ModelingLanguage(UML)
RationalUnifiedProcess(RUP)
CapabilityMaturityModel(CMM)Big Design
Up-Front(BDUF)
Desarrolloen Cascada
COCOMO
viernes 9 de abril de 2010
![Page 9: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/9.jpg)
Desarrollo Tradicional
viernes 9 de abril de 2010
![Page 10: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/10.jpg)
2001viernes 9 de abril de 2010
![Page 11: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/11.jpg)
Mike Beedle
Arie van Bennekum
Alistair Cockburn
Ward Cunningham
Martin FowlerJim Highsmith
Andy Hunt
Ron Jeffries
Jon Kern
Brian Marick
Robert C. MartinUncle Bob
Ken Schwaber
Jeff Sutherland
Dave Thomas
viernes 9 de abril de 2010
![Page 12: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/12.jpg)
XP Programming
Scrum
Kanbanviernes 9 de abril de 2010
![Page 13: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/13.jpg)
Manifesto for Agile Software Development
viernes 9 de abril de 2010
![Page 14: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/14.jpg)
Manifesto for Agile Software Development
Individuals and interactions over processes and tools
viernes 9 de abril de 2010
![Page 15: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/15.jpg)
Manifesto for Agile Software Development
Individuals and interactions over processes and tools
Working software over comprehensive documentation
viernes 9 de abril de 2010
![Page 16: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/16.jpg)
Manifesto for Agile Software Development
Individuals and interactions over processes and tools
Working software over comprehensive documentation
Customer collaboration over contract negotiation
viernes 9 de abril de 2010
![Page 17: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/17.jpg)
Manifesto for Agile Software Development
Individuals and interactions over processes and tools
Working software over comprehensive documentation
Customer collaboration over contract negotiation
Responding to change over following a plan
viernes 9 de abril de 2010
![Page 18: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/18.jpg)
Manifesto for Agile Software Development
Individuals and interactions over processes and tools
Working software over comprehensive documentation
Customer collaboration over contract negotiation
Responding to change over following a plan
That is, while there is value in the items on the right, we value the items on the left more.
viernes 9 de abril de 2010
![Page 19: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/19.jpg)
Manifesto for Agile Software Development
Individuals and interactions over processes and tools
Working software over comprehensive documentation
Customer collaboration over contract negotiation
Responding to change over following a plan
That is, while there is value in the items on the right, we value the items on the left more.
http://agilemanifesto.org
viernes 9 de abril de 2010
![Page 20: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/20.jpg)
Desarrollo ágil
• Programación por parejas
• Historias de usuario
• La metáfora del sistema
• Clientes in situ
• Unidades de prueba
• Desarrollo dirigido por pruebas
• Refactorización
• Diseño simple
• Iteraciones cortas
• Propiedad colectiva del código
• Reflexión continua
• Integración continua
viernes 9 de abril de 2010
![Page 21: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/21.jpg)
¿Qué es una prueba unitaria?
• Una unidad de prueba (unit test) es un trozo de código (típicamente un método) que invoca a otro trozo de código y comprueba después la corrección de algunas asunciones.
• Si las asunciones no eran válidas, se dice que la unidad de prueba ha fallado.
• Una unidad (unit) es un método o función.
viernes 9 de abril de 2010
![Page 22: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/22.jpg)
El sistema a prueba
• El sistema sometido a prueba (system under test) recibe el nombre de SUT.
• Hay quien llama CUT a la clase sometida a prueba.
viernes 9 de abril de 2010
![Page 23: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/23.jpg)
No confundir
• Prueba de aceptación: el programa supera una demanda del cliente
• Prueba de integración
• Prueba de regresión
• Prueba de prestaciones
• Prueba de carga
• Prueba de estrés
viernes 9 de abril de 2010
![Page 24: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/24.jpg)
Buenos “unit test”
• Automatizable y repetible
• Fácil de implementar
• Debe permanecer, una vez se ha escrito
• Cualquiera debe poder ejecutarlo
• Ejecutarlo debe ser sencillo
• Debe ser rápido
viernes 9 de abril de 2010
![Page 25: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/25.jpg)
Frameworks
• Los frameworks de unit testing permiten:
• Simplificar el diseño de pruebas unitarias
• Facilitar un entorno para la ejecución de las pruebas
• Proporcionar informes de los resultados
viernes 9 de abril de 2010
![Page 26: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/26.jpg)
Frameworks
• xUnit:
• JUnit: Java
• NUnit: C#
• PyUnit: Python
• Test::Unit: Ruby
• ...
viernes 9 de abril de 2010
![Page 27: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/27.jpg)
The various meanings of TDD
• 1) Test Driven Development: the idea of writing your code in a test first manner. You may already have an existing design in place.
• 2) Test Oriented Development: Having unit tests of integration tests in your code and write them out either before or after our write the code. Your code has lots of tests. You recognize the value of tests but you don't necessarily write them before you write the code. Design probably exists before you write the code
• 3) Test Driven Design(the eXtreme Programming way): The idea of using a test-first approach as a fully fledged design technique, where tests are a bonus but the idea is to drive full design from little to no design whatsoever. You design as you go.
• 4) Test Driven Development and Design: using the test-first technique to drive new code and changes, while also allowing it to change and evolve your design as an added bonus. You may already have some design in place before starting to code, but it could very well change because the tests point out various smells.
http://weblogs.asp.net/rosherove/archive/2007/10/08/the-various-meanings-of-tdd.aspx
viernes 9 de abril de 2010
![Page 28: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/28.jpg)
viernes 9 de abril de 2010
![Page 29: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/29.jpg)
Desarrollo dirigido por las pruebas (TDD)
viernes 9 de abril de 2010
![Page 30: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/30.jpg)
More NUnit attributes 39
!"#$%&'()*' In NUnit, an ignored test is marked in yellow (the middle test), and the reason for not running the test is listed under the Tests Not Run tab on the right.
It can look like this:
[Test][Ignore("there is a problem with this test")] public void IsValidFileName_ValidFile_ReturnsTrue() { /// ... }
Running this test in the NUnit GUI will produce a result like that shown in figure 2.6.
What happens when you want to have tests running not by a namespace but by some other type of grouping? That’s where test cate-gories come in.
()+), -&.."/#'.&0.'12.%"&0
You can set up your tests to run under specific test categories, such as slow tests and fast tests. You do this by using NUnit’s [Category] attri-bute:
[Test][Category("Fast Tests")] public void IsValidFileName_ValidFile_ReturnsTrue() { /// ... }
viernes 9 de abril de 2010
![Page 31: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/31.jpg)
¿Con qué herramientas hacemos TDD con
Python?
viernes 9 de abril de 2010
![Page 32: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/32.jpg)
Python
• Versión 3.1.2
• http://www.python.org
viernes 9 de abril de 2010
![Page 33: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/33.jpg)
unittest (PyUnit)
• Viene de serie con Python 3.1
viernes 9 de abril de 2010
![Page 34: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/34.jpg)
Eclipse
• Versión 3.5.2, Galileo
• http://eclipse.org
viernes 9 de abril de 2010
![Page 35: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/35.jpg)
• Versión 1.5.6
• http://pydev.org
• Instalación: http://pydev.org/updates
viernes 9 de abril de 2010
![Page 36: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/36.jpg)
Pydev
viernes 9 de abril de 2010
![Page 37: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/37.jpg)
Una demo
viernes 9 de abril de 2010
![Page 38: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/38.jpg)
Sudoku
• Queremos un programa que permita jugar a Sudokus
• En aras de la brevedad, Sudokus de 4x4
viernes 9 de abril de 2010
![Page 39: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/39.jpg)
Historias de usuario
1. Dada una lista con 4 filas de números, saber si describe un Sudoku de 4x4
2. Dada una cadena con 4 líneas de caracteres entre 1 y 4 y asteriscos en posición libre, obtener las lista que lo describe
3. Resolver automáticamente un Sudoku
4. Jugar partidas contra un jugador humano
viernes 9 de abril de 2010
![Page 40: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/40.jpg)
Creación de un proyecto Pydev
• File:: New:: Pydev project
viernes 9 de abril de 2010
![Page 41: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/41.jpg)
viernes 9 de abril de 2010
![Page 42: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/42.jpg)
Asociar una gramática Python e intérprete
• Menu contextual del proyecto
• Properties
• PyDev - Interpreter/Grammar
• Configurar un intérprete
viernes 9 de abril de 2010
![Page 43: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/43.jpg)
Fijar perspectiva Pydev
• Open Perspective :: Other... ::
viernes 9 de abril de 2010
![Page 44: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/44.jpg)
Empezamos
• Ye hemos creado el proyecto Sudoku
• Contendrá una carpeta src
• Dentro creamos un package para las pruebas (menú contextual en el proyecto: New :: Pydev package) y le llamamos test
viernes 9 de abril de 2010
![Page 45: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/45.jpg)
Historias de usuario
1. Dada una lista con 4 filas de números, saber si describe un Sudoku de 4x4
2. Dada una cadena con 4 líneas de caracteres entre 1 y 4 y asteriscos en posición libre, obtener las lista que lo describe
3. Resolver automáticamente un Sudoku
4. Jugar partidas contra un jugador humano
viernes 9 de abril de 2010
![Page 46: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/46.jpg)
Pruebas de aceptación1. Dada una lista con 4 filas de números, saber si describe un
Sudoku de 4x4
1.1. Rechaza una “no lista”
1.2. Rechaza lista que no es de 4x4
1.3. Rechaza lista que no contiene 4x4 enteros
1.4. Se suministra una lista de 4x4 con números menores que 0 o mayores que 4 y la rechaza
1.5. Rechaza lista con repetidos en fila
1.6. Rechaza lista con repetidos en columna
1.7. Rechaza lista con repetidos en región 2x2
viernes 9 de abril de 2010
![Page 47: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/47.jpg)
• Dentro creamos un package para las pruebas (menú contextual en el proyecto: New :: Pydev package) y le llamamos test
• En test creamos un módulo Python de tipo Unittest: test_SudokuValidator
• Y creamos el primer ¿método de test?
Manos a la obra
viernes 9 de abril de 2010
![Page 48: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/48.jpg)
'''Created on 06/04/2010
@author: amarzal'''import unittest
class Test(unittest.TestCase):
def testName(self): pass
if __name__ == "__main__": #import sys;sys.argv = ['', 'Test.testName'] unittest.main()
viernes 9 de abril de 2010
![Page 49: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/49.jpg)
from unittest import TestCase
class TestSudokuValidator(TestCase): def test_sudoku_isNotAList_isRejected(self): validator = SudokuValidator() sudoku = 0 self.assertFalse(validator.check(sudoku), "Acepta una no lista")
viernes 9 de abril de 2010
![Page 50: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/50.jpg)
from unittest import TestCase
class TestSudokuValidator(TestCase): def test_sudoku_isNotAList_isRejected(self): validator = SudokuValidator() sudoku = 0 self.assertFalse(validator.check(sudoku), "Acepta una no lista")
TestCase: Clase cuyos métodos son
tests
viernes 9 de abril de 2010
![Page 51: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/51.jpg)
from unittest import TestCase
class TestSudokuValidator(TestCase): def test_sudoku_isNotAList_isRejected(self): validator = SudokuValidator() sudoku = 0 self.assertFalse(validator.check(sudoku), "Acepta una no lista")
TestCase: Clase cuyos métodos son
tests
Método de test: empieza por test_
viernes 9 de abril de 2010
![Page 52: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/52.jpg)
from unittest import TestCase
class TestSudokuValidator(TestCase): def test_sudoku_isNotAList_isRejected(self): validator = SudokuValidator() sudoku = 0 self.assertFalse(validator.check(sudoku), "Acepta una no lista")
TestCase: Clase cuyos métodos son
tests
Método de test: empieza por test_
SUT
viernes 9 de abril de 2010
![Page 53: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/53.jpg)
from unittest import TestCase
class TestSudokuValidator(TestCase): def test_sudoku_isNotAList_isRejected(self): validator = SudokuValidator() sudoku = 0 self.assertFalse(validator.check(sudoku), "Acepta una no lista")
TestCase: Clase cuyos métodos son
tests
Método de test: empieza por test_
SUT
Aserto
viernes 9 de abril de 2010
![Page 54: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/54.jpg)
from unittest import TestCase
class TestSudokuValidator(TestCase): def test_sudoku_isNotAList_isRejected(self): validator = SudokuValidator() sudoku = 0 self.assertFalse(validator.check(sudoku), "Acepta una no lista")
TestCase: Clase cuyos métodos son
tests
Método de test: empieza por test_
SUT
Aserto Mensaje de fallo
viernes 9 de abril de 2010
![Page 55: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/55.jpg)
¿Pasa la prueba?
• Evidentemente, no puede pasarlo. El SUT no existe aún.
• Pero veamos cómo falla:
• Menú contextual en test_SudokuValidator.py, Run As :: Python unit-test_SudokuValidator.py
viernes 9 de abril de 2010
![Page 56: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/56.jpg)
Finding files...['/Users/amarzal/Documents/workspace/Sudoku/src/tests/test_SudokuValidator.py'] ... doneImporting test modules ... done.
test_sudoku_isNotAList_isRejected (test_SudokuValidator.TestSudokuValidator) ... ERROR
======================================================================ERROR: test_sudoku_isNotAList_isRejected (test_SudokuValidator.TestSudokuValidator)----------------------------------------------------------------------Traceback (most recent call last): File "/Users/amarzal/Documents/workspace/Sudoku/src/tests/test_SudokuValidator.py", line 7, in test_sudoku_isNotAList_isRejected validator = SudokuValidator()NameError: global name 'SudokuValidator' is not defined
----------------------------------------------------------------------Ran 1 test in 0.001s
FAILED (errors=1)
viernes 9 de abril de 2010
![Page 57: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/57.jpg)
A por el SUT• En src creamos un package: sudoku
• En el package creamos un módulo: validator
• En ese módulo definimos la clase SudokuValidator con el método check
class SudokuValidator: def check(self, sudoku): if not isinstance(sudoku, list): return False return True
viernes 9 de abril de 2010
![Page 58: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/58.jpg)
from unittest import TestCasefrom sudoku.validator import SudokuValidator
class TestSudokuValidator(TestCase): def test_sudoku_isNotAList_isRejected(self): validator = SudokuValidator() sudoku = 0 self.assertFalse(validator.check(sudoku), "Acepta una no lista")
viernes 9 de abril de 2010
![Page 59: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/59.jpg)
Finding files...['/Users/amarzal/Documents/workspace/Sudoku/src/tests/test_SudokuValidator.py'] ... doneImporting test modules ... done.
test_sudoku_isNotAList_isRejected (test_SudokuValidator.TestSudokuValidator) ... ok
----------------------------------------------------------------------Ran 1 test in 0.000s
OK
viernes 9 de abril de 2010
![Page 60: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/60.jpg)
Tests de aceptación1. Dada una lista con 4 filas de números, saber si describe un
Sudoku de 4x4
1.1. Rechaza una “no lista”
1.2. Rechaza lista que no es de 4x4
1.3. Rechaza lista que no contiene 4x4 enteros
1.4. Se suministra una lista de 4x4 con números menores que 0 o mayores que 4 y la rechaza
1.5. Rechaza lista con repetidos en fila
1.6. Rechaza lista con repetidos en columna
1.7. Rechaza lista con repetidos en región 2x2
viernes 9 de abril de 2010
![Page 61: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/61.jpg)
from unittest import TestCasefrom sudoku.validator import SudokuValidator
class TestSudokuValidator(TestCase): def test_sudoku_isNotAList_isRejected(self): validator = SudokuValidator() sudoku = 0 self.assertFalse(validator.check(sudoku)) def test_sudoku_isNotA4x4List_isRejected(self): validator = SudokuValidator() sudoku = [] self.assertFalse(validator.check(sudoku))
Los mensajes pueden ser superfluos si se
nombran bien los test
viernes 9 de abril de 2010
![Page 62: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/62.jpg)
Finding files...['/Users/amarzal/Documents/workspace/Sudoku/src/tests/test_SudokuValidator.py'] ... doneImporting test modules ... done.
test_sudoku_isNotA4x4List_isRejected (test_SudokuValidator.TestSudokuValidator) ... FAILtest_sudoku_isNotAList_isRejected (test_SudokuValidator.TestSudokuValidator) ... ok
======================================================================FAIL: test_sudoku_isNotA4x4List_isRejected (test_SudokuValidator.TestSudokuValidator)----------------------------------------------------------------------Traceback (most recent call last): File "/Users/amarzal/Documents/workspace/Sudoku/src/tests/test_SudokuValidator.py", line 15, in test_sudoku_isNotA4x4List_isRejected self.assertFalse(validator.check(sudoku))AssertionError: True is not False
----------------------------------------------------------------------Ran 2 tests in 0.001s
FAILED (failures=1)
viernes 9 de abril de 2010
![Page 63: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/63.jpg)
class SudokuValidator: def check(self, sudoku): if not isinstance(sudoku, list): return False
if len(sudoku) != 4: return False for row in sudoku: if not isinstance(sudoku, list): return False if len(row) != 4: return False
return Trueviernes 9 de abril de 2010
![Page 64: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/64.jpg)
Finding files...['/Users/amarzal/Documents/workspace/Sudoku/src/tests/test_SudokuValidator.py'] ... doneImporting test modules ... done.
test_sudoku_isNotA4x4List_isRejected (test_SudokuValidator.TestSudokuValidator) ... oktest_sudoku_isNotAList_isRejected (test_SudokuValidator.TestSudokuValidator) ... ok
----------------------------------------------------------------------Ran 2 tests in 0.000s
OK
viernes 9 de abril de 2010
![Page 65: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/65.jpg)
from unittest import TestCasefrom sudoku.validator import SudokuValidator
class TestSudokuValidator(TestCase): def test_sudoku_isNotAList_isRejected(self): validator = SudokuValidator() sudoku = 0 self.assertFalse(validator.check(sudoku)) def test_sudoku_isNotA4x4List_isRejected(self): validator = SudokuValidator() sudoku = [] self.assertFalse(validator.check(sudoku))
viernes 9 de abril de 2010
![Page 66: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/66.jpg)
from unittest import TestCasefrom sudoku.validator import SudokuValidator
class TestSudokuValidator(TestCase):
def setUp(self): self.validator = SudokuValidator() def test_sudoku_isNotAList_isRejected(self): sudoku = 0 self.assertFalse(self.validator.check(sudoku)) def test_sudoku_isNotA4x4List_isRejected(self): sudoku = [] self.assertFalse(self.validator.check(sudoku))
viernes 9 de abril de 2010
![Page 67: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/67.jpg)
Tests de aceptación1. Dada una lista con 4 filas de números, saber si describe un
Sudoku de 4x4
1.1. Rechaza una “no lista”
1.2. Rechaza lista que no es de 4x4
1.3. Rechaza lista que no contiene 4x4 enteros
1.4. Se suministra una lista de 4x4 con números menores que 0 o mayores que 4 y la rechaza
1.5. Rechaza lista con repetidos en fila
1.6. Rechaza lista con repetidos en columna
1.7. Rechaza lista con repetidos en región 2x2
viernes 9 de abril de 2010
![Page 68: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/68.jpg)
class SudokuValidator: def check(self, sudoku): if not isinstance(sudoku, list): return False if len(sudoku) != 4: return False for row in sudoku: if not isinstance(sudoku, list): return False if len(row) != 4: return False for row in sudoku: for number in row: if not isinstance(number, int): return False for row in sudoku: for number in row: if not (0 <= number <= 4): return False
for i in range(4): numbers = set() for j in range(4): n = sudoku[i][j] if n != 0 and n in numbers: return False numbers.add(n) for j in range(4): numbers = set() for i in range(4): n = sudoku[i][j] if n != 0 and n in numbers: return False numbers.add(n) for region in (((0,0), (0,1), (1,0), (1,1)), ((0,2), (0,3), (1,2), (1,3)), ((2,0), (2,1), (3,0), (3,1)), ((2,2), (2,3), (3,2), (3,3))): numbers = set() for (i, j) in region: n = sudoku[i][j] if n != 0 and n in numbers: return False numbers.add(n) return True
viernes 9 de abril de 2010
![Page 69: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/69.jpg)
¡Hora de refactorizar!
• Pero ya no saltamos sin red:
podemos refactorizar con la tranquilidad de que los tests “nos vigilan”
viernes 9 de abril de 2010
![Page 70: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/70.jpg)
class SudokuValidator: def __init__(self): rows = tuple(tuple((i, j) for j in range(4)) for i in range(4)) columns = tuple(tuple((i, j) for i in range(4)) for j in range(4)) sectors = (((0,0), (0,1), (1,0), (1,1)), ((0,2), (0,3), (1,2), (1,3)), ((2,0), (2,1), (3,0), (3,1)), ((2,2), (2,3), (3,2), (3,3))) self.regions = rows + columns + sectors def check(self, sudoku): if not isinstance(sudoku, list) or len(sudoku) != 4: return False for row in sudoku: if not isinstance(sudoku, list) or len(row) != 4: return False for number in row: if not (isinstance(number, int) and 0 <= number <= 4): return False for region in self.regions: numbers = set() for (i, j) in region: n = sudoku[i][j] if n != 0 and n in numbers: return False numbers.add(n) return True
viernes 9 de abril de 2010
![Page 71: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/71.jpg)
Finding files...['/Users/amarzal/Documents/workspace/Sudoku/src/tests/test_SudokuValidator.py'] ... doneImporting test modules ... done.
test_sudoku_isNotA4x4List_isRejected (test_SudokuValidator.TestSudokuValidator) ... oktest_sudoku_isNotAList_isRejected (test_SudokuValidator.TestSudokuValidator) ... oktest_sudoku_withNotIntegers_isRejected (test_SudokuValidator.TestSudokuValidator) ... oktest_sudoku_withOutOfRangeNumbers_isRejected (test_SudokuValidator.TestSudokuValidator) ... oktest_sudoku_withRepeatedNumbersInColumn_isRejected (test_SudokuValidator.TestSudokuValidator) ... oktest_sudoku_withRepeatedNumbersInRow_isRejected (test_SudokuValidator.TestSudokuValidator) ... oktest_sudoku_withRepeatedNumbersInSector_isRejected (test_SudokuValidator.TestSudokuValidator) ... ok
----------------------------------------------------------------------Ran 7 tests in 0.001s
OK
viernes 9 de abril de 2010
![Page 72: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/72.jpg)
Un poco de “teoría”• Las pruebas deben ser
• Legibles
• Fácilmente ejecutables
• Rápidos
• Sin estado
• Las pruebas guían el diseño y evitan la sobreingeniería: YAGNI (You ain’t gonna need it)
• La refactorización es más segura: los cambios se prueban instantáneamente contra una batería de pruebas de aceptación
viernes 9 de abril de 2010
![Page 73: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/73.jpg)
Historias de usuario
1. Dada una lista con 4 filas de números, saber si describe un Sudoku de 4x4
2. Dada una cadena con 4 líneas de caracteres entre 1 y 4 y asteriscos en posición libre, obtener las lista que lo describe
3. Resolver automáticamente un Sudoku
4. Jugar partidas contra un jugador humano
viernes 9 de abril de 2010
![Page 74: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/74.jpg)
Pruebas de aceptación2. Dada una cadena con 4 líneas de caracteres entre 1 y 4 y
asteriscos en posición libre, obtener las lista que lo describe
2.1. Si se pasa un dato que no es una cadena, debe rechazarla
2.2. Si la cadena no tiene 4 líneas de 4 caracteres, debe rechazarla
2.3. Si tiene caracteres diferentes de ‘1’, ‘2’, ‘3’, ‘4’ o ‘*’ en las líneas, debe rechazarla
2.4. Si se le pasa una cadena correcta, debe proporcionar la lista de lista correspondiente
viernes 9 de abril de 2010
![Page 75: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/75.jpg)
import unittestfrom sudoku.parser import SudokuParser
class TestSudokuParser(unittest.TestCase): def setUp(self): self.parser = SudokuParser() def test_parses_withNonString_returnsParseException(self): unproper_sudoku = 0 self.assertRaises(TypeError, self.parser.parse, unproper_sudoku)
def test_parses_unproperSizeString_returnsParseException(self): unproper_sudoku="""***************""" self.assertRaises(ValueError, self.parser.parse, unproper_sudoku)
def test_parses_invalidChars_returnsParseException(self): unproper_sudoku="""***************=""" self.assertRaises(ValueError, self.parser.parse, unproper_sudoku)
def test_parses_validSudoku_returnsSudoku(self): sudoku="""1****2****3****4""" self.assertEquals(self.parser.parse(sudoku), [[1,0,0,0], [0,2,0,0], [0,0,3,0], [0,0,0,4]])
if __name__ == "__main__": #import sys;sys.argv = ['', 'Test.testName'] unittest.main()
viernes 9 de abril de 2010
![Page 76: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/76.jpg)
Historias de usuario
1. Dada una lista con 4 filas de números, saber si describe un Sudoku de 4x4
2. Dada una cadena con 4 líneas de caracteres entre 1 y 4 y asteriscos en posición libre, obtener las lista que lo describe
3. Resolver automáticamente un Sudoku
4. Jugar partidas contra un jugador humano
viernes 9 de abril de 2010
![Page 77: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/77.jpg)
Historias de usuario
3. Resolver automáticamente un Sudoku
3.1. Dado un sudoku completo, devolverlo tal cual
3.2. Dado un sudoku incompleto, devolverlo resuelto
viernes 9 de abril de 2010
![Page 78: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/78.jpg)
import unittestfrom sudoku.solver import SudokuSolver
class TestSudokuSolver(unittest.TestCase):
def setUp(self): self.solver = SudokuSolver() self.sudoku = [[0,0,4,0],[1,0,0,0], [0,0,0,3], [0,1,0,0]] self.solution = [[[2, 3, 4, 1], [1, 4, 3, 2], [4, 2, 1, 3], [3, 1, 2, 4]]] def test_solver_withCompleteSudoku_returnSameSudoku(self): self.assertEquals(list(self.solver.solve(self.solution[0])), self.solution)
def test_solver_withValidSudoku_returnSolution(self): self.assertEquals(list(self.solver.solve(self.sudoku)), self.solution)
if __name__ == "__main__": #import sys;sys.argv = ['', 'Test.testName'] unittest.main()
viernes 9 de abril de 2010
![Page 79: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/79.jpg)
from copy import deepcopy
class SudokuSolver: def __init__(self): self.rows = tuple(tuple((i, j) for j in range(4)) for i in range(4)) self.columns = tuple(tuple((i, j) for i in range(4)) for j in range(4)) sectors = (((0,0), (0,1), (1,0), (1,1)), ((0,2), (0,3), (1,2), (1,3)), ((2,0), (2,1), (3,0), (3,1)), ((2,2), (2,3), (3,2), (3,3))) self.sector = {} for sector in sectors: for (i, j) in sector: self.sector[i, j] = sector self.regions = self.rows + self.columns + sectors
def solve(self, sudoku): return self._backtrack(deepcopy(sudoku), 0, 0)
def _backtrack(self, sudoku, i, j): for n in self._options(sudoku, i, j): old_value, sudoku[i][j] = sudoku[i][j], n sudoku[i][j] = n if self._has_next(i, j): for solution in self._backtrack(sudoku, *self._next(i, j)): yield solution else: yield deepcopy(sudoku) sudoku[i][j] = old_value
viernes 9 de abril de 2010
![Page 80: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/80.jpg)
import unittestfrom sudoku.solver import SudokuSolver
class TestSudokuSolver(unittest.TestCase):
def setUp(self): self.solver = SudokuSolver() self.sudoku = [[0,0,4,0],[1,0,0,0], [0,0,0,3], [0,1,0,0]] self.solution = [[[2, 3, 4, 1], [1, 4, 3, 2], [4, 2, 1, 3], [3, 1, 2, 4]]] def test_solver_withCompleteSudoku_returnSameSudoku(self): self.assertEquals(list(self.solver.solve(self.solution[0])), self.solution)
def test_solver_withValidSudoku_returnSolution(self): self.assertEquals(list(self.solver.solve(self.sudoku)), self.solution)
def test_options_ofFreeCell_areEnumerated(self): options = set(self.solver._options(self.sudoku, 0, 0)) self.assertEquals(options, {2,3})
def test_options_ofCellWithNumber_isTheNumber(self): options = set(self.solver._options(self.sudoku, 0, 2)) self.assertEquals(options, {4})
def test_hasNext_beforeLast_returnTrue(self): self.assertTrue(self.solver._has_next(3, 2))
def test_hasNext_atLast_returnTrue(self): self.assertFalse(self.solver._has_next(3, 3))
def test_next_fromOrigin_iteratesAllCellIndices(self): (x, y) = (0, 0) for i in range(4): for j in range(4): self.assertEquals((x,y), (i,j)) if self.solver._has_next(x, y): (x, y) = self.solver._next(x, y)
if __name__ == "__main__": #import sys;sys.argv = ['', 'Test.testName'] unittest.main()
viernes 9 de abril de 2010
![Page 81: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/81.jpg)
class SudokuSolver:
...
def _has_next(self, i, j): if i < 3: return True if j < 3: return True return False
def _next(self, i, j): if j < 3: return (i, j+1) if i < 3: return (i+1, 0) def _options(self, sudoku, i, j): if sudoku[i][j] != 0: yield sudoku[i][j] else: used = set(sudoku[x][y] for (x, y) in self.rows[i] + self.columns[j] \ + self.sector[i, j]) for n in set(range(1, 5)) - used: yield n
viernes 9 de abril de 2010
![Page 82: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/82.jpg)
Historias de usuario
1. Dada una lista con 4 filas de números, saber si describe un Sudoku de 4x4
2. Dada una cadena con 4 líneas de caracteres entre 1 y 4 y asteriscos en posición libre, obtener las lista que lo describe
3. Resolver automáticamente un Sudoku
4. Jugar partidas contra un jugador humano
viernes 9 de abril de 2010
![Page 83: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/83.jpg)
Nos faltas historias de usuario
• Ahora caemos en que hemos de preparar una visualización apropiada para el Sudoku
• ¿Cómo?
viernes 9 de abril de 2010
![Page 84: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/84.jpg)
Historias de usuario1. Dada una lista con 4 filas de números, saber si
describe un Sudoku de 4x4
2. Dada una cadena con 4 líneas de caracteres entre 1 y 4 y asteriscos en posición libre, obtener las lista que lo describe
3. Resolver automáticamente un Sudoku
4. Presentar gráficamente los Sudoku
5. Jugar partidas contra un jugador humano
viernes 9 de abril de 2010
![Page 85: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/85.jpg)
import unittestfrom sudoku.presenter import SudokuPresenter
class TestSudokuPresenter(unittest.TestCase):
def setUp(self): self.presenter = SudokuPresenter()
def test_show_validSudoku_returnValidString(self): sudoku = [[0, 0, 3, 0], [4, 0, 1, 0], [0, 1, 4, 3], [0, 0, 0, 0]] presentation = """ 1 2 3 4 +-+-+-+-+1 | | |3| | +-+-+-+-+2 |4| |1| | +-+-+-+-+3 | |1|4|3| +-+-+-+-+4 | | | | | +-+-+-+-+""" self.assertEquals(self.presenter.show(sudoku), presentation)
if __name__ == "__main__": #import sys;sys.argv = ['', 'Test.testName'] unittest.main()
viernes 9 de abril de 2010
![Page 86: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/86.jpg)
class SudokuPresenter: def show(self, sudoku): s = [] s.append(" 1 2 3 4") for (i, row) in enumerate(sudoku): s.append(" +-+-+-+-+") s.append("{} |".format(i+1)) for number in row: r = " " if number == 0 else repr(number) s[-1] += r + "|" s.append(" +-+-+-+-+") return '\n'.join(s)
viernes 9 de abril de 2010
![Page 87: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/87.jpg)
Historias de usuario1. Dada una lista con 4 filas de números, saber si
describe un Sudoku de 4x4
2. Dada una cadena con 4 líneas de caracteres entre 1 y 4 y asteriscos en posición libre, obtener las lista que lo describe
3. Resolver automáticamente un Sudoku
4. Presentar gráficamente los Sudoku
5. Jugar partidas contra un jugador humano
viernes 9 de abril de 2010
![Page 88: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/88.jpg)
¿Cómo hacemos pruebas con un humano?
• Se usan impostores (dobles de prueba) para simular el acceso a recursos:
• de producción y/o que interactúan con el entorno
• demasiado lentos para las pruebas
• tan complejos que podrían fallar y llamarnos a engaño sobre el responsable del fallo
viernes 9 de abril de 2010
![Page 89: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/89.jpg)
Herramientas
• Mockito for Python
• Mockito es un framework para Java, con port para Python 3.1
• http://code.google.com/p/mockito/
• http://code.google.com/p/mockito/wiki/MockitoForPython
viernes 9 de abril de 2010
![Page 90: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/90.jpg)
Un jugador “impostable”
• Vamos a diseñar una clase que modela al jugador
• El jugador introducirá coordenadas de la casilla que quiere modificar y el número que quiere poner en esa casilla (0 será borrar)
viernes 9 de abril de 2010
![Page 91: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/91.jpg)
class SudokuPlayer(): def get_coordinates_and_number(self): while True: print("Play i j n:", end="") line = input().strip() words = line.split() if len(words) == 3: try: i = int(words[0]) j = int(words[1]) n = int(words[2]) if 1 <= i <= 4 and 1 <= j <= 4 and 0 <= n <= 9: return (i-1, j-1, n) else: raise ValueError() except ValueError: print("Invalid input")
Feo: imprime en pantalla, lee de teclado.Difícil de impostar: no hay costuras (seams)
viernes 9 de abril de 2010
![Page 92: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/92.jpg)
class SudokuPlayer(): def __init__(self, prompt=lambda: print("Play i j n:", end=""), notify_error = lambda: print("Invalid input"), get_input=input): self.prompt = prompt self.notify_error = notify_error self.get_input = get_input def get_coordinates_and_number(self): while True: self.prompt() line = self.get_input().strip() words = line.split() if len(words) == 3: try: i = int(words[0]) j = int(words[1]) n = int(words[2]) if 1 <= i <= 4 and 1 <= j <= 4 and 0 <= n <= 9: return (i-1, j-1, n) else: raise ValueError() except ValueError: self.notify_error()
Podemos impostar la propia I/Oviernes 9 de abril de 2010
![Page 93: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/93.jpg)
import unittestfrom sudoku.player import SudokuPlayerfrom io import StringIOfrom mockito import *
class TestSudokuPlayer(unittest.TestCase):
def test_getCoordinatesAndNumber_readsGoodUserValues_returnsProperValues(self): ioMock = Mock() #@UndefinedVariable when(ioMock).get_input().thenReturn("1 1 1") #@UndefinedVariable output = StringIO() player = SudokuPlayer(prompt=lambda: None, notify_error=lambda: None, get_input=ioMock.get_input) (i, j, n) = player.get_coordinates_and_number() verify(ioMock, times=1).get_input() #@UndefinedVariable self.assertEquals((i,j,n), (0,0,1)) output.close()
def test_getCoordinatesAndNumber_readsBadAndGoodUserValues_returnsProperValues(self): ioMock = Mock() #@UndefinedVariable when(ioMock).get_input().thenReturn("1 1 100").thenReturn("1 1 1") #@UndefinedVariable output = StringIO() player = SudokuPlayer(prompt=lambda: None, notify_error=lambda: None, get_input=ioMock.get_input) (i, j, n) = player.get_coordinates_and_number() verify(ioMock, times=2).get_input() #@UndefinedVariable self.assertEquals((i,j,n), (0, 0, 1)) output.close()
def test_getCoordinatesAndNumber_readsBadAndGoodUserValues_errorIsNotified(self): ioMock = Mock() #@UndefinedVariable when(ioMock).notify_error().thenReturn("ERROR") #@UndefinedVariable when(ioMock).get_input().thenReturn("1 1 100").thenReturn("1 1 1") #@UndefinedVariable output = StringIO() player = SudokuPlayer(prompt=lambda: None, notify_error=ioMock.notify_error, get_input=ioMock.get_input) (i, j, n) = player.get_coordinates_and_number() verify(ioMock, times=1).notify_error() #@UndefinedVariable output.close() if __name__ == "__main__": #import sys;sys.argv = ['', 'Test.testName'] unittest.main()
viernes 9 de abril de 2010
![Page 94: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/94.jpg)
class TestSudokuPlayer(unittest.TestCase):
def test_getCoordinatesAndNumber_readsGoodUserValues_returnsProperValues(self): ioMock = Mock() #@UndefinedVariable when(ioMock).get_input().thenReturn("1 1 1") #@UndefinedVariable output = StringIO() player = SudokuPlayer(prompt=lambda: None, notify_error=lambda: None, get_input=ioMock.get_input) (i, j, n) = player.get_coordinates_and_number() verify(ioMock, times=1).get_input() #@UndefinedVariable self.assertEquals((i,j,n), (0,0,1)) output.close()
verificación
Fake/Stub
Mock
Fake/Stub
viernes 9 de abril de 2010
![Page 95: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/95.jpg)
Mocks aren’t Stubs (Martin Fowler)
• Dummy objects are passed around but never actually used. Usually they are just used to fill parameter lists.
• Fake objects actually have working implementations, but usually take some shortcut which makes them not suitable for production (an in memory database is a good example).
• Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what's programmed in for the test. Stubs may also record information about calls, such as an email gateway stub that remembers the messages it 'sent', or maybe only how many messages it 'sent'.
• Mocks are what we are talking about here: objects pre-programmed with expectations which form a specification of the calls they are expected to receive.
viernes 9 de abril de 2010
![Page 96: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/96.jpg)
import unittestfrom sudoku.game import SudokuGamefrom sudoku.player import SudokuPlayerfrom mockito import *
class TestSudokuGame(unittest.TestCase):
def test_game_withInvalidSudokuString_raisesException(self): game = SudokuGame(sudoku_chooser=lambda x: "") self.assertRaises(ValueError, game.start, None)
def test_game_withIncompleteSudoku_raisesException(self): game = SudokuGame(sudoku_chooser=lambda x: "234\n341*\n214*\n*321") self.assertRaises(ValueError, game.start, None)
def test_game_withImpossibleSudoku_raisesException(self): game = SudokuGame(sudoku_chooser=lambda x: "2234\n341*\n214*\n*321") self.assertRaises(ValueError, game.start, None)
def test_game_withValidSudoku_plasyOK(self): player = Mock() #@UndefinedVariable when(player).get_coordinates_and_number().thenReturn((0,0,1)) \ .thenReturn((1,3,2)) \ .thenReturn((2,3,3)) \ .thenReturn((3,0,4))
game = SudokuGame(sudoku_chooser=lambda x: "*234\n341*\n214*\n*321") self.assertTrue(game.start(player))
if __name__ == "__main__": #import sys;sys.argv = ['', 'Test.testName'] unittest.main()
viernes 9 de abril de 2010
![Page 97: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/97.jpg)
from sudoku.solver import SudokuSolverfrom sudoku.presenter import SudokuPresenterfrom sudoku.validator import SudokuValidatorfrom sudoku.parser import SudokuParserfrom sudoku.player import SudokuPlayer
from random import choicefrom copy import deepcopy
class SudokuGame: def __init__(self, sudoku_chooser=lambda sudokus: choice(sudokus)): self.parser = SudokuParser() self.validator = SudokuValidator() self.solver = SudokuSolver() self.presenter = SudokuPresenter() self.sudoku_chooser = sudoku_chooser self.sudokus = ["2143\n4**1\n1**4\n3412", "*234\n341*\n214*\n*321", "*23*\n1**4\n2**3\n*14*", "4**2\n*31*\n*42*\n3**1", "4*1*\n1*2*\n*4*1\n*1*2", "12**\n**21\n24**\n**42", "*13*\n****\n****\n3421", "*3*1\n**2*\n**3*\n*4*2", "****\n12**\n3***\n***1", "**3*\n****\n*2**\n*14*"] def start(self, player): sudoku_string = self.sudoku_chooser(self.sudokus) sudoku = self.parser.parse(sudoku_string) if not self.validator.check(sudoku): raise ValueError("Invalid Sudoku\n" + self.presenter.show(sudoku)) original_sudoku = deepcopy(sudoku) print(self.presenter.show(sudoku)) while not self.validator.complete(sudoku): (i, j, n) = player.get_coordinates_and_number() if sudoku[i][j] == 0: sudoku[i][j] = n if not self.validator.check(sudoku): sudoku[i][j] = 0 elif n == 0 and original_sudoku[i][j] == 0: sudoku[i][j] = 0 print(self.presenter.show(sudoku)) return True if __name__ == "__main__": game = SudokuGame() game.start(SudokuPlayer())
viernes 9 de abril de 2010
![Page 98: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/98.jpg)
from sudoku.solver import SudokuSolverfrom sudoku.presenter import SudokuPresenterfrom sudoku.validator import SudokuValidatorfrom sudoku.parser import SudokuParserfrom sudoku.player import SudokuPlayer
from random import choicefrom copy import deepcopy
class SudokuGame: def __init__(self, sudoku_chooser=lambda sudokus: choice(sudokus)): self.parser = SudokuParser() self.validator = SudokuValidator() self.solver = SudokuSolver() self.presenter = SudokuPresenter() self.sudoku_chooser = sudoku_chooser self.sudokus = ["2143\n4**1\n1**4\n3412", "*234\n341*\n214*\n*321", "*23*\n1**4\n2**3\n*14*", "4**2\n*31*\n*42*\n3**1", "4*1*\n1*2*\n*4*1\n*1*2", "12**\n**21\n24**\n**42", "*13*\n****\n****\n3421", "*3*1\n**2*\n**3*\n*4*2", "****\n12**\n3***\n***1", "**3*\n****\n*2**\n*14*"] def start(self, player): sudoku_string = self.sudoku_chooser(self.sudokus) sudoku = self.parser.parse(sudoku_string) if not self.validator.check(sudoku): raise ValueError("Invalid Sudoku\n" + self.presenter.show(sudoku)) original_sudoku = deepcopy(sudoku) print(self.presenter.show(sudoku)) while not self.validator.complete(sudoku): (i, j, n) = player.get_coordinates_and_number() if sudoku[i][j] == 0: sudoku[i][j] = n if not self.validator.check(sudoku): sudoku[i][j] = 0 elif n == 0 and original_sudoku[i][j] == 0: sudoku[i][j] = 0 print(self.presenter.show(sudoku)) return True if __name__ == "__main__": game = SudokuGame() game.start(SudokuPlayer())
viernes 9 de abril de 2010
![Page 99: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/99.jpg)
Pruebas de cobertura
• ¿Hemos puesto a prueba todas y cada una de las líneas de nuestro código?
• Muy importante en lenguajes dinámicos
viernes 9 de abril de 2010
![Page 100: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/100.jpg)
Herramientas
• coverage.py
• Versión 3.3.1
• Instalación: easy_install coverage
• http://nedbatchelder.com/code/coverage/
viernes 9 de abril de 2010
![Page 101: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/101.jpg)
import osimport unittestimport coverageimport importlibfrom unittest import TestResult
def find_test_paths(startDir="test"): result = [] directories = [startDir] while len(directories)>0: directory = directories.pop() for name in os.listdir(directory): fullpath = os.path.join(directory,name) if os.path.isfile(fullpath) and \ name.startswith("test"): result.append(fullpath) elif os.path.isdir(fullpath): directories.append(fullpath) return result
test_paths = find_test_paths()
cov = coverage.coverage()cov.start()
loaded = set()suite = unittest.TestSuite()for module in test_paths: mod = importlib.import_module(''.join(
module.split(".")[:-1]).replace("/", ".")) exec("import {}".format(mod.__name__)) for c in dir(mod): if c.startswith("Test"): fullname = mod.__name__ + "." + c testclass = eval(fullname) if testclass not in loaded: loaded.add(testclass) suite.addTest(unittest.TestLoader()\ .loadTestsFromTestCase(testclass))
result = TestResult()suite.run(result)print(result)cov.stop()cov.report()
viernes 9 de abril de 2010
![Page 102: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/102.jpg)
<unittest.TestResult run=52 errors=0 failures=0>Name Stmts Exec Cover Missing---------------------------------------------------------sudoku/__init__ 1 1 100% sudoku/game 35 30 85% 40-42, 47-48sudoku/parser 16 16 100% sudoku/player 20 20 100% sudoku/presenter 12 12 100% sudoku/solver 36 36 100% sudoku/validator 36 34 94% 17, 44test/__init__ 1 1 100% test/test_SudokuGame 21 20 95% 33test/test_SudokuParser 19 18 94% 38test/test_SudokuPlayer 34 33 97% 49test/test_SudokuPresenter 11 10 90% 27test/test_SudokuSolver 30 29 96% 41test/test_SudokuValidator 28 27 96% 39---------------------------------------------------------TOTAL 300 287 95%
viernes 9 de abril de 2010
![Page 103: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/103.jpg)
algoritmiaUna librería de estructuras de datos, algoritmos clásicos
y esquemas algorítmicos
MIT License
viernes 9 de abril de 2010
![Page 104: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/104.jpg)
Mercurial
• Versión 1.5
• http://mercurial.selenic.com
viernes 9 de abril de 2010
![Page 105: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/105.jpg)
HgEclipse
• Versión 1.5
• http://www.javaforge.com/project/HGE
• Instalación: http://hge.javaforge.com/hgeclipse
viernes 9 de abril de 2010
![Page 106: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/106.jpg)
viernes 9 de abril de 2010
![Page 107: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/107.jpg)
CodePlex
• Repositorio Mercurial con el proyecto algoritmia
• http://algoritmica.codeplex.com
viernes 9 de abril de 2010
![Page 108: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/108.jpg)
Clonar algoritmia
viernes 9 de abril de 2010
![Page 109: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/109.jpg)
viernes 9 de abril de 2010
![Page 110: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/110.jpg)
Importar algoritmia
viernes 9 de abril de 2010
![Page 111: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/111.jpg)
viernes 9 de abril de 2010
![Page 112: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/112.jpg)
Libros
M A N N I N G
the art of
with Examples in .NET
ROY O SHER OVE
ptg
From the Library of Lee Bogdanoff
this print for content only—size & color not accurate spine = 0.7904" 416 page count
BOOKS FOR PROFESSIONALS BY PROFESSIONALS®
Foundations of Agile Python DevelopmentDear Reader,
Python is your chosen development language. You love its power, clarity, and interactivity. But what is the best way to build and maintain Python applications? How can you blend its unique strengths with the best of agile methods to reach still higher levels of productivity and quality? And, at a practical level, where are the tools to automate it all? In this book, I give answers to these questions, backed up by a wealth of down-to-earth examples and working code.
The short development cycles of agile projects require far more automation than traditional processes. There’s simply no way to have a two-week release cycle if development involves a day of integration, a week of QA, and three days for production deployment. You must automate to succeed. But all too often, the best-known tools are language specific. For this reason, this book gives you a complete set of open source tools to turbocharge your Python projects, and shows you how to integrate them into a smoothly functioning whole.
Eclipse and Pydev make an excellent Python IDE. Python ships with an xUnit-based unit-testing framework. Nose is great for running tests, supplemented by PyFit for functional testing. Setuptools is your build harness and packaging mechanism, with functionality similar to Maven in Java. Subversion provides a place to store your code, and Buildbot is an ideal continuous integration server. What makes this book different from others is that I show you how to tie all of these pieces together into one continuous tool chain that builds your software from start to finish—fast!
While the information I present is steeped in the language of agile develop-ment, the details are not limited to that approach. This book is as much about release engineering in Python as it is about agile development.
Jeff Younker
US $42.99
Shelve in Python
User level: Intermediate–Advanced
YounkerFoundations of Agile Python Developm
ent
THE EXPERT’S VOICE® IN OPEN SOURCE
Foundations of
Agile Python Development
CYAN MAGENTA
YELLOW BLACK PANTONE 123 C
Jeff Younker
Companion eBook Available
THE APRESS ROADMAP
Beginning Python:From Novice to Professional
Foundations of PythonNetwork Programming
Foundations of Agile Python Development
Dive into Python
www.apress.comSOURCE CODE ONLINE
Companion eBook
See last page for details
on $10 eBook version
ISBN-13: 978-1-59059-981-5ISBN-10: 1-59059-981-0
9 781590 599815
54299
Python, agile project methods, and a comprehensive open source tool chain!
viernes 9 de abril de 2010
![Page 113: TDD Con Python](https://reader034.vdocument.in/reader034/viewer/2022052308/563db810550346aa9a90327e/html5/thumbnails/113.jpg)
Code and have fun!¡Gracias por vuestra atención!
viernes 9 de abril de 2010