2010-06-24 5 views
5

Mi marco de prueba se basa actualmente en una utilidad de corrector de prueba que a su vez se deriva del pitón Eclipse pydev Test-Runner. Voy a usar Nose, que tiene muchas de las funciones de mi versión de prueba personalizada, pero parece ser un código de mejor calidad.¿Es posible hacer que Nariz solo ejecute pruebas que son subclases de TestCase o TestSuite (como unittest.main())

Mi conjunto de pruebas incluye una serie de clases de prueba abstractas que anteriormente nunca se ejecutaron. La versión estándar de python testrunner (y la mía personalizada) solo ejecutaba instancias de unittest.TestCase y unittest.TestSuite.

He notado que desde que me cambié a Nose se está ejecutando casi cualquier cosa que comience con el nombre "prueba" que es molesto ... porque la convención de nomenclatura que utilizamos para la prueba-mixins también parece una clase de prueba para Nariz. Anteriormente, estos nunca se ejecutaban como pruebas porque no eran instancias de TestCase o TestSuite.

Obviamente podría renombrar los métodos para excluir la palabra "prueba" de sus nombres ... eso tomaría un tiempo porque el marco de prueba es muy grande y tiene mucha herencia. Por otro lado, sería estupendo si hubiera una manera de hacer que Nose solo vea que TestCases y TestSuites son ejecutables ... y nada más.

¿Se puede hacer esto?

Respuesta

5

Puede intentar jugar con la opción -m para nosetests. A partir de la documentación:

Una clase de prueba es una clase definida en un módulo prueba que coincide testMatch o es una subclase de unittest.TestCase

-m conjuntos que testMatch, de esta manera se puede desactivar la prueba cualquier cosa que comience con la prueba .

Otra cosa es que puede agregar __test__ = False a su declaración de clase de caso de prueba, para marcarlo como "no es una prueba".

+0

El 'truco __test__ = false' no es muy útil en el caso de una clase padre, esto obligaría a los niños clases para especificar explícitamente' __test__ = TRUE o sería ignorada, que es un peligroso truco para usar. – Guibod

0

Puede usar el argumento --attr de nose para especificar un atributo poseído por el unittest.TestCase. Por ejemplo, yo uso:

nosetests --attr="assertAlmostEqual" 

usted podría conseguir aún más cuidadoso utilizando y y o juego:

nosetests -A "assertAlmostEqual or addTest" 

Ver unittest's documentation para obtener una lista completa de métodos/atributos, y la nariz del description of the capabilities of the --attr plugin.

2

Si quieres una clase de prueba verdaderamente abstracta, puedes simplemente heredar la clase abstracta de un objeto y luego heredarla en las cajas de prueba más tarde.

Por ejemplo:

class AbstractTestcases(object): 
    def test_my_function_does_something(self): 
     self.assertEquals("bar", self.func()) 

y luego usarlo con:

class TestMyFooFunc(AbstractTestcases, unittest.TestCase): 
    def setUp(self): 
     self.func = lambda: "foo" 

Entonces nosetests recogerá sólo los casos de prueba en TestMyFooFunc y no los de AbstractTestCases.

+0

Esa es una buena forma de evitar el problema, pero la prueba abstracta es bastante ilegible en mi IDE. – Guibod

0

Una adición a la respuesta @nailxx 's:

También podemos establecer __test__ = False en la clase padre y luego utilizar una metaclase (ver This question con algunas explicaciones brillantes) para configurarlo de nuevo a True cuando la subclasificación.

(Finalmente, encontré una excusa para usar una metaclase!)

Aunque __test__ es un atributo de subrayado doble, tenemos que establecer explícitamente que True, ya que no se establece que causaría pitón sólo para las operaciones de búsqueda el atributo más arriba en el MRO y evaluarlo a False.

Por lo tanto, debemos verificar en instanciación de clase si una de las clases principales tiene __test__ = False. Si este es el caso y la definición de clase actual no ha establecido __test__ en sí, agregaremos '__test__': True a los atributos dict.

El código resultante es el siguiente:

class TestWhenSubclassedMeta(type): 
    """Metaclass that sets `__test__` back to `True` when subclassed. 

    Usage: 

     >>> class GenericTestCase(TestCase, metaclass=TestWhenSubclassed): 
     ...  __test__ = False 
     ... 
     ...  def test_something(self): 
     ...   self.fail("This test is executed in a subclass, only.") 
     ... 
     ... 
     >>> class SpecificTestCase(GenericTestCase): 
     ...  pass 

    """ 

    def __new__(mcs, name, bases, attrs): 
     ATTR_NAME = '__test__' 
     VALUE_TO_RESET = False 
     RESET_VALUE = True 

     values = [getattr(base, ATTR_NAME) for base in bases 
        if hasattr(base, ATTR_NAME)] 

     # only reset if the first attribute is `VALUE_TO_RESET` 
     try: 
      first_value = values[0] 
     except IndexError: 
      pass 
     else: 
      if first_value == VALUE_TO_RESET and ATTR_NAME not in attrs: 
       attrs[ATTR_NAME] = RESET_VALUE 

     return super().__new__(mcs, name, bases, attrs) 

Se podría extender esto a un comportamiento más implícita como “si el nombre comienza con Abstract, establecer __test__ = False automáticamente”, pero para mí sería mantener la asignación explícita para mayor claridad.


Let Me pego unittests simples para demostrar el comportamiento - y como un recordatorio de que todo el mundo debería tener los dos minutos para probar su código después de la introducción de una función.

from unittest import TestCase 

from .base import TestWhenSubclassedMeta 


class SubclassesTestCase(TestCase): 
    def test_subclass_resetted(self): 
     class Base(metaclass=TestWhenSubclassedMeta): 
      __test__ = False 

     class C(Base): 
      pass 

     self.assertTrue(C.__test__) 
     self.assertIn('__test__', C.__dict__) 

    def test_subclass_not_resetted(self): 
     class Base(metaclass=TestWhenSubclassedMeta): 
      __test__ = True 

     class C(Base): 
      pass 

     self.assertTrue(C.__test__) 
     self.assertNotIn('__test__', C.__dict__) 

    def test_subclass_attr_not_set(self): 
     class Base(metaclass=TestWhenSubclassedMeta): 
      pass 

     class C(Base): 
      pass 

     with self.assertRaises(AttributeError): 
      getattr(C, '__test__') 
0

También puede utilizar la herencia múltiple en el nivel de casos de prueba y dejar que la hereda de la clase base de solamente object. Ver this thread:

class MyBase(object): 
    def finishsetup(self): 
     self.z=self.x+self.y 

    def test1(self): 
     self.assertEqual(self.z, self.x+self.y) 

    def test2(self): 
     self.assert_(self.x > self.y) 

class RealCase1(MyBase, unittest.TestCase): 
    def setUp(self): 
     self.x=10 
     self.y=5 
     MyBase.finishsetup(self) 

class RealCase2(MyBase, unittest.TestCase): 
    def setUp(self): 
     self.x=42 
     self.y=13 
     MyBase.finishsetup(self) 
Cuestiones relacionadas