run - unittest python
¿Cómo hago para que nose descubra testcases generados dinámicamente? (3)
Podría tratar de generar las clases de testcase con type ()
class UnderTest_MixIn(object):
def f1(self, i):
return i + 1
def f2(self, i):
return i + 2
SomeDynamicTestcase = type(
"SomeDynamicTestcase",
(UnderTest_MixIn, unittest.TestCase),
{"even_more_dynamic":"attributes .."}
)
# or even:
name = ''SomeDynamicTestcase''
globals()[name] = type(
name,
(UnderTest_MixIn, unittest.TestCase),
{"even_more_dynamic":"attributes .."}
)
Esto debería crearse cuando nose intenta importar tu test_module para que funcione.
La ventaja de este enfoque es que puede crear muchas combinaciones de pruebas dinámicamente.
Este es un seguimiento de una pregunta anterior mía.
En la pregunta anterior, se exploraron los métodos para implementar lo que esencialmente era la misma prueba en una familia completa de funciones, asegurando que las pruebas no terminaran en la primera función que falló.
Mi solución preferida usó una metaclase para insertar dinámicamente las pruebas en una prueba unitaria. Prueba de caso. Desafortunadamente, la nariz no capta esto porque la nariz explora de forma estática los casos de prueba.
¿Cómo me sale la nariz para descubrir y ejecutar un TestCase? Consulte aquí un ejemplo de la TestCase en cuestión.
Nose tiene una función de "generador de prueba" para cosas como esta. Usted escribe una función de generador que produce cada función de "caso de prueba" que desea que se ejecute, junto con sus argumentos. Siguiendo su ejemplo anterior, esto podría verificar cada una de las funciones en una prueba separada:
import unittest
import numpy
from somewhere import the_functions
def test_matrix_functions():
for function in the_functions:
yield check_matrix_function, function
def check_matrix_function(function)
matrix1 = numpy.ones((5,10))
matrix2 = numpy.identity(5)
output = function(matrix1, matrix2)
assert matrix1.shape == output.shape, /
"%s produces output of the wrong shape" % str(function)
Nose no analiza las pruebas de forma estática, por lo que puede usar la magia de las metaclases para realizar las pruebas que encuentra Nose.
La parte difícil es que las técnicas estándar de metaclases no configuran el atributo func_name correctamente, que es lo que busca Nose cuando verifica si los métodos de su clase son pruebas.
Aquí hay una metaclase simple. Se ve a través de la función y agrega un nuevo método para cada método que encuentra, afirmando que el método que encontró tiene una docstring. Estos nuevos métodos sintéticos reciben los nombres "test_%d" %i
.
import new
from inspect import isfunction, getdoc
class Meta(type):
def __new__(cls, name, bases, dct):
newdct = dct.copy()
for i, (k, v) in enumerate(filter(lambda e: isfunction(e[1]), dct.items())):
def m(self, func):
assert getdoc(func) is not None
fname = ''test_%d'' % i
newdct[fname] = new.function(m.func_code, globals(), fname,
(v,), m.func_closure)
return super(Meta, cls).__new__(cls, ''Test_''+name, bases, newdct)
Ahora, creemos una nueva clase que use esta metaclase
class Foo(object):
__metaclass__ = Meta
def greeter(self):
"sdf"
print ''Hello World''
def greeter_no_docstring(self):
pass
En tiempo de ejecución, Foo
se llamará Test_Foo
y tendrá greeter
, greeter_no_docstring
, test_1
y test_2
como sus métodos. Cuando ejecuto nosetests
en este archivo, aquí está el resultado:
$ nosetests -v test.py
test.Test_Foo.test_0 ... FAIL
test.Test_Foo.test_1 ... ok
======================================================================
FAIL: test.Test_Foo.test_0
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Library/Frameworks/EPD64.framework/Versions/7.3/lib/python2.7/site-packages/nose/case.py", line 197, in runTest
self.test(*self.arg)
File "/Users/rmcgibbo/Desktop/test.py", line 10, in m
assert getdoc(func) is not None
AssertionError
----------------------------------------------------------------------
Ran 2 tests in 0.002s
FAILED (failures=1)
Esta metaclase no es realmente útil tal como es, pero si en cambio usas el Meta
no como una metaclase adecuada, sino como una metaclase funcional (es decir, toma una clase como argumento y devuelve una nueva clase, una que se renombró para que la nariz lo encontrará), entonces es útil. He utilizado este enfoque para probar automáticamente que las cadenas de documentos se adhieren al estándar Numpy como parte de un conjunto de pruebas de nariz.
Además, he tenido muchos problemas para lograr un cierre adecuado trabajando con new.function, por lo que este código usa m(self, func)
donde func
está hecho para ser un argumento predeterminado. Sería más natural usar un cierre sobre el value
, pero eso no parece funcionar.