2008-12-07 12 views
6

Esto es una continuación de un previous question mío.¿Cómo hago para que nose descubra testcases generados dinámicamente?

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 de la unidad. 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 here para obtener un ejemplo del TestCase en cuestión.

+0

¿Has probado mi solución? No se basa en ninguna magia, la suite resultante debe ser reconocible. – muhuk

+0

Tengo, ver comentario en el hilo original. Tal vez hay una opción de configuración para ello, o alguna forma de configurar un "objetivo ficticio" para la nariz? – saffsd

+1

Por cierto, he resuelto este problema sin recurrir al generador de prueba de la nariz (está limitado que no se puede usar para las subclases de 'unittest.TestCase'). Ver: http://stackoverflow.com/questions/5176396/nose-unittest-testcase-and-metaclass-auto-generated-test-methods-not-discover/5177625#5177625 – Santa

Respuesta

7

La nariz 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 comprobar cada una de las funciones en un ensayo separado:

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) 
0

Se podría tratar de generar las clases TestCase con el tipo()

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 se debe crear cuando se trata de la nariz para importar su test_module así que debería funcionar.

La ventaja de este enfoque es que puede crear muchas combinaciones de pruebas dinámicamente.

2

nariz no busca pruebas de forma estática, por lo que puede uso magia metaclase para hacer pruebas de que la nariz se encuentra.

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 comprueba 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, vamos a crear una nueva clase que utiliza este 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 en realidad será nombrado Test_Foo y tendrá greeter, greeter_no_docstring, test_1 y test_2 como sus métodos. Cuando corro nosetests en este archivo, aquí está la salida:

$ 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) 

Este metaclase no es realmente útil como es, pero si en lugar de utilizar el Meta no como una metaclase adecuada, pero más como una metaclase funcional (es decir, toma una clase como argumento y devuelve una nueva clase, una que se renombró para que la nariz la encuentre), 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 el cierre adecuado trabajando con los nuevos.función, razón por la cual 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 value, pero eso no parece funcionar.

Cuestiones relacionadas