TDD Con Python
TDD Con Python
Development
con Python
y un ejemplo: la librera algoritmia
Andrs Marzal
Universitat Jaume I
amarzal@lsi.uji.es
Martin Fowler
Guin
Qu es TDD?
Con qu herramientas hacemos TDD con Python?
Una demo
unitest
mockito
coverage
Algunas conclusiones
Algoritmia
Qu es TDD?
Desarrollo Tradicional
Desarrollo Tradicional
Desarrollo
en Cascada
Rational
Unified
Process
(RUP)
COCOMO
Big Design
Up-Front
(BDUF)
Unified
Modeling
Language
(UML)
Capability
Maturity
Model
(CMM)
Desarrollo Tradicional
2001
viernes 9 de abril de 2010
Andy Hunt
Robert C. Martin
Uncle Bob
Brian Marick
Arie van Bennekum
Jeff Sutherland
Ron Jeffries
Ken Schwaber
Martin Fowler
Jim Highsmith
Alistair Cockburn
Mike Beedle
Jon Kern
XP Programming
Scrum
Kanban
viernes 9 de abril de 2010
Desarrollo gil
Refactorizacin
Diseo simple
Iteraciones cortas
Propiedad colectiva del cdigo
Reflexin continua
Integracin continua
Qu es una prueba
unitaria?
El sistema a prueba
El sistema sometido a prueba (system under
test) recibe el nombre de SUT.
No confundir
Prueba de aceptacin: el programa supera
una demanda del cliente
Prueba de integracin
Prueba de regresin
Prueba de prestaciones
Prueba de carga
Prueba de estrs
viernes 9 de abril de 2010
Frameworks
Los frameworks de unit testing permiten:
Simplificar el diseo de pruebas unitarias
Facilitar un entorno para la ejecucin de
las pruebas
Frameworks
xUnit:
JUnit: Java
NUnit: C#
PyUnit: Python
Test::Unit: Ruby
...
viernes 9 de abril de 2010
http://weblogs.asp.net/rosherove/archive/2007/10/08/the-various-meanings-of-tdd.aspx
viernes 9 de abril de 2010
!"#$%&'()*' 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.
39
Con qu herramientas
hacemos TDD con
Python?
Python
Versin 3.1.2
http://www.python.org
unittest (PyUnit)
Eclipse
Versin 3.5.2, Galileo
http://eclipse.org
Versin 1.5.6
http://pydev.org
Instalacin: http://pydev.org/updates
Pydev
Una demo
Sudoku
Queremos un programa que permita jugar
a Sudokus
Historias de usuario
1. Dada una lista con 4 filas de nmeros, saber
si describe un Sudoku de 4x4
2. Dada una cadena con 4 lneas de caracteres
entre 1 y 4 y asteriscos en posicin libre,
obtener las lista que lo describe
3. Resolver automticamente un Sudoku
4. Jugar partidas contra un jugador humano
Creacin de un
proyecto Pydev
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
Historias de usuario
1. Dada una lista con 4 filas de nmeros, saber
si describe un Sudoku de 4x4
2. Dada una cadena con 4 lneas de caracteres
entre 1 y 4 y asteriscos en posicin libre,
obtener las lista que lo describe
3. Resolver automticamente un Sudoku
4. Jugar partidas contra un jugador humano
Pruebas de aceptacin
1. Dada una lista con 4 filas de nmeros, 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 nmeros 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 regin 2x2
Manos a la obra
Dentro creamos un package para las
'''
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()
TestCase: Clase
cuyos mtodos son
tests
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 mtodos son
tests
class TestSudokuValidator(TestCase):
def test_sudoku_isNotAList_isRejected(self):
validator = SudokuValidator()
sudoku = 0
self.assertFalse(validator.check(sudoku),
"Acepta una no lista")
Mtodo de test:
empieza por test_
viernes 9 de abril de 2010
TestCase: Clase
cuyos mtodos son
tests
class TestSudokuValidator(TestCase):
def test_sudoku_isNotAList_isRejected(self):
validator = SudokuValidator()
SUT
sudoku = 0
self.assertFalse(validator.check(sudoku),
"Acepta una no lista")
Mtodo de test:
empieza por test_
viernes 9 de abril de 2010
TestCase: Clase
cuyos mtodos son
tests
class TestSudokuValidator(TestCase):
def test_sudoku_isNotAList_isRejected(self):
validator = SudokuValidator()
SUT
sudoku = 0
self.assertFalse(validator.check(sudoku),
"Acepta una no lista")
Mtodo de test:
empieza por test_
viernes 9 de abril de 2010
Aserto
TestCase: Clase
cuyos mtodos son
tests
class TestSudokuValidator(TestCase):
def test_sudoku_isNotAList_isRejected(self):
validator = SudokuValidator()
SUT
sudoku = 0
self.assertFalse(validator.check(sudoku),
"Acepta una no lista")
Mtodo de test:
empieza por test_
viernes 9 de abril de 2010
Aserto
Mensaje de fallo
Pasa la prueba?
Evidentemente, no puede pasarlo. El SUT no
existe an.
Finding files...
['/Users/amarzal/Documents/workspace/Sudoku/src/tests/test_SudokuValidator.py'] ... done
Importing 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)
A por el SUT
Finding files...
['/Users/amarzal/Documents/workspace/Sudoku/src/tests/test_SudokuValidator.py'] ... done
Importing test modules ... done.
test_sudoku_isNotAList_isRejected (test_SudokuValidator.TestSudokuValidator) ... ok
---------------------------------------------------------------------Ran 1 test in 0.000s
OK
Tests de aceptacin
1. Dada una lista con 4 filas de nmeros, 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 nmeros 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 regin 2x2
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))
Finding files...
['/Users/amarzal/Documents/workspace/Sudoku/src/tests/test_SudokuValidator.py'] ... done
Importing test modules ... done.
test_sudoku_isNotA4x4List_isRejected (test_SudokuValidator.TestSudokuValidator) ... FAIL
test_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)
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 True
viernes 9 de abril de 2010
Finding files...
['/Users/amarzal/Documents/workspace/Sudoku/src/tests/test_SudokuValidator.py'] ... done
Importing test modules ... done.
test_sudoku_isNotA4x4List_isRejected (test_SudokuValidator.TestSudokuValidator) ... ok
test_sudoku_isNotAList_isRejected (test_SudokuValidator.TestSudokuValidator) ... ok
---------------------------------------------------------------------Ran 2 tests in 0.000s
OK
Tests de aceptacin
1. Dada una lista con 4 filas de nmeros, 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 nmeros 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 regin 2x2
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),
((0,2), (0,3), (1,2),
((2,0), (2,1), (3,0),
((2,2), (2,3), (3,2),
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
(1,1)),
(1,3)),
(3,1)),
(3,3))):
Hora de refactorizar!
Pero ya no saltamos sin red:
podemos refactorizar con la tranquilidad de
que los tests nos vigilan
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
Finding files...
['/Users/amarzal/Documents/workspace/Sudoku/src/tests/test_SudokuValidator.py'] ... done
Importing test modules ... done.
test_sudoku_isNotA4x4List_isRejected (test_SudokuValidator.TestSudokuValidator) ... ok
test_sudoku_isNotAList_isRejected (test_SudokuValidator.TestSudokuValidator) ... ok
test_sudoku_withNotIntegers_isRejected (test_SudokuValidator.TestSudokuValidator) ... ok
test_sudoku_withOutOfRangeNumbers_isRejected (test_SudokuValidator.TestSudokuValidator) ... ok
test_sudoku_withRepeatedNumbersInColumn_isRejected (test_SudokuValidator.TestSudokuValidator) ... ok
test_sudoku_withRepeatedNumbersInRow_isRejected (test_SudokuValidator.TestSudokuValidator) ... ok
test_sudoku_withRepeatedNumbersInSector_isRejected (test_SudokuValidator.TestSudokuValidator) ... ok
---------------------------------------------------------------------Ran 7 tests in 0.001s
OK
Un poco de teora
Legibles
Fcilmente ejecutables
Rpidos
Sin estado
Historias de usuario
1. Dada una lista con 4 filas de nmeros, saber
si describe un Sudoku de 4x4
2. Dada una cadena con 4 lneas de caracteres
entre 1 y 4 y asteriscos en posicin libre,
obtener las lista que lo describe
3. Resolver automticamente un Sudoku
4. Jugar partidas contra un jugador humano
Pruebas de aceptacin
2. Dada una cadena con 4 lneas de caracteres entre 1 y 4 y
asteriscos en posicin 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 lneas de 4 caracteres, debe
rechazarla
2.3. Si tiene caracteres diferentes de 1, 2, 3, 4 o * en las
lneas, debe rechazarla
2.4. Si se le pasa una cadena correcta, debe proporcionar la
lista de lista correspondiente
import unittest
from 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
Historias de usuario
1. Dada una lista con 4 filas de nmeros, saber
si describe un Sudoku de 4x4
2. Dada una cadena con 4 lneas de caracteres
entre 1 y 4 y asteriscos en posicin libre,
obtener las lista que lo describe
3. Resolver automticamente un Sudoku
4. Jugar partidas contra un jugador humano
Historias de usuario
3. Resolver automticamente un Sudoku
3.1. Dado un sudoku completo, devolverlo
tal cual
3.2. Dado un sudoku incompleto, devolverlo
resuelto
import unittest
from 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()
import unittest
from 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)
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
Historias de usuario
1. Dada una lista con 4 filas de nmeros, saber
si describe un Sudoku de 4x4
2. Dada una cadena con 4 lneas de caracteres
entre 1 y 4 y asteriscos en posicin libre,
obtener las lista que lo describe
3. Resolver automticamente un Sudoku
4. Jugar partidas contra un jugador humano
Cmo?
Historias de usuario
1. Dada una lista con 4 filas de nmeros, saber si
describe un Sudoku de 4x4
2. Dada una cadena con 4 lneas de caracteres
entre 1 y 4 y asteriscos en posicin libre,
obtener las lista que lo describe
3. Resolver automticamente un Sudoku
4. Presentar grficamente los Sudoku
5. Jugar partidas contra un jugador humano
import unittest
from sudoku.presenter import SudokuPresenter
class TestSudokuPresenter(unittest.TestCase):
def setUp(self):
self.presenter = SudokuPresenter()
1
2
3
4
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
+-+-+-+-+
| | |3| |
+-+-+-+-+
|4| |1| |
+-+-+-+-+
| |1|4|3|
+-+-+-+-+
| | | | |
+-+-+-+-+"""
self.assertEquals(self.presenter.show(sudoku), presentation)
if __name__ == "__main__":
#import sys;sys.argv = ['', 'Test.testName']
unittest.main()
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)
Historias de usuario
1. Dada una lista con 4 filas de nmeros, saber si
describe un Sudoku de 4x4
2. Dada una cadena con 4 lneas de caracteres
entre 1 y 4 y asteriscos en posicin libre,
obtener las lista que lo describe
3. Resolver automticamente un Sudoku
4. Presentar grficamente los Sudoku
5. Jugar partidas contra un jugador humano
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
Un jugador
impostable
Vamos a disear una clase que modela al
jugador
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")
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()
import unittest
from sudoku.player import SudokuPlayer
from io import StringIO
from 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']
viernes 9 de abril de 2010
class TestSudokuPlayer(unittest.TestCase):
def test_getCoordinatesAndNumber_readsGoodUserValues_returnsProperValues(self):
ioMock = Mock() #@UndefinedVariable
when(ioMock).get_input().thenReturn("1 1 1") #@UndefinedVariable
Fake/Stub
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()
Fake/Stub
Mock
verificacin
import unittest
from sudoku.game import SudokuGame
from sudoku.player import SudokuPlayer
from 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))
from
from
from
from
from
"**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())
Pruebas de cobertura
Hemos puesto a prueba todas y cada una
de las lneas de nuestro cdigo?
Herramientas
coverage.py
Versin 3.3.1
Instalacin: easy_install coverage
http://nedbatchelder.com/code/coverage/
viernes 9 de abril de 2010
import os
import unittest
import coverage
import importlib
from 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()
algoritmia
Una librera de estructuras de datos, algoritmos clsicos
y esquemas algortmicos
MIT License
Mercurial
Versin 1.5
http://mercurial.selenic.com
HgEclipse
Versin 1.5
http://www.javaforge.com/project/HGE
Instalacin: http://hge.javaforge.com/
hgeclipse
CodePlex
Repositorio Mercurial con el proyecto
algoritmia
http://algoritmica.codeplex.com
Clonar algoritmia
Importar algoritmia
Libros
From the Library of Lee Bogdanoff
R OY O SHER OVE
MANNING
Foundations of
the art of
Foundations of
Agile Python
Development
Python, agile project methods, and a
comprehensive open source tool chain!
Jeff Younker