ValueError: no existe tal método de prueba en : runTest

Tengo un caso de prueba:

class LoginTestCase(unittest.TestCase): ... 

Me gustaría usarlo en un caso de prueba diferente:

 class EditProfileTestCase(unittest.TestCase): def __init__(self): self.t = LoginTestCase() self.t.login() 

Esto plantea:

 ValueError: no such test method in <class 'LoginTest: runTest` 

Miré el código de prueba de unidad donde se llama la excepción, y parece que las pruebas no deben escribirse de esta manera. ¿Existe una forma estándar de escribir algo que le gustaría que se hiciera la prueba para que pueda reutilizarse en pruebas posteriores? ¿O hay una solución alternativa?

He agregado un método runTest vacío a LoginTest como una solución dudosa por ahora.

unittest hace magia negra profunda: si eliges usarlo para ejecutar tus pruebas unitarias (yo sí, ya que de esta manera puedo usar una batería muy poderosa de corredores de prueba & c integrada en el sistema de comstackción en mi lugar de trabajo, pero definitivamente hay alternativas que valgan la pena), será mejor que sigas sus reglas.

En este caso, simplemente tendría que EditProfileTestCase derive de LoginTestCase (en lugar de directamente desde unittest.TestCase ). Si hay algunas partes de LoginTestCase que también desea probar en el entorno diferente de EditProfileTestCase , y otras que no lo hace, es una cuestión simple refactorar LoginTestCase en esas dos partes (posiblemente usando herencia múltiple) y, si es así, algunas cosas. Es necesario que suceda de forma ligeramente diferente en los dos casos, factoréelos en los “métodos de enganche” auxiliares (en un patrón de diseño de “Método de plantilla”). Utilizo todos estos enfoques a menudo para disminuir la caldera y boost la reutilización en las pruebas de unidad copiosa. siempre escriba (si tengo una cobertura de prueba unitaria <95%, siempre me siento incómodo, por debajo del 90%, empiezo a sentirme físicamente enfermo ;-).

La confusión con “runTest” se basa principalmente en el hecho de que esto funciona:

 class MyTest(unittest.TestCase): def test_001(self): print "ok" if __name__ == "__main__": unittest.main() 

Por lo tanto, no hay “runTest” en esa clase y se está llamando a todas las funciones de prueba. Sin embargo, si observa la clase base “TestCase” (lib / python / unittest / case.py), encontrará que tiene un argumento “methodName” que por defecto es “runTest” pero NO tiene una implementación predeterminada de ” def runTest ”

 class TestCase: def __init__(self, methodName='runTest'): 

La razón por la que unittest.main funciona bien se basa en el hecho de que no necesita “runTest”; puede imitar el comportamiento creando una instancia de subclase TestCase para todos los métodos que tenga en su subclase; simplemente proporcione el nombre como primer argumento:

 class MyTest(unittest.TestCase): def test_001(self): print "ok" if __name__ == "__main__": suite = unittest.TestSuite() for method in dir(MyTest): if method.startswith("test"): suite.addTest(MyTest(method)) unittest.TextTestRunner().run(suite) 

Aquí hay algo de ‘ magia negra profunda ‘:

 suite = unittest.TestLoader().loadTestsFromTestCase(Test_MyTests) unittest.TextTestRunner(verbosity=3).run(suite) 

Muy útil si solo quieres probar tus pruebas de unidad desde un shell (es decir, IPython ).

Si no le importa editar directamente el código del módulo de unidad de prueba, la solución simple es agregar bajo la clase test.ase de case.py un nuevo método llamado runTest que no hace nada.

El archivo a editar se encuentra en pythoninstall \ Lib \ unittest \ case.py

 def runTest(self): pass 

Esto evitará que recibas este error.

La respuesta de Guido está casi ahí, sin embargo no explica la cosa. Necesitaba mirar el código de unittest de unittest para captar el flujo.

Digamos que tienes lo siguiente.

 import unittest class MyTestCase(unittest.TestCase): def testA(self): pass def testB(self): pass 

Cuando use unittest.main() , intentará descubrir casos de prueba en el módulo actual. El código importante es unittest.loader.TestLoader.loadTestsFromTestCase .

 def loadTestsFromTestCase(self, testCaseClass): # ... # This will look in class' callable attributes that start # with 'test', and return their names sorted. testCaseNames = self.getTestCaseNames(testCaseClass) # If there's no test to run, look if the case has the default method. if not testCaseNames and hasattr(testCaseClass, 'runTest'): testCaseNames = ['runTest'] # Create TestSuite instance having test case instance per test method. loaded_suite = self.suiteClass(map(testCaseClass, testCaseNames)) return loaded_suite 

Lo que hace este último es convertir la clase de caso de prueba en un conjunto de pruebas, que contiene las instancias de la clase según su método de prueba. Es decir, mi ejemplo se convertirá en unittest.suite.TestSuite([MyTestCase('testA'), MyTestCase('testB')]) . Por lo tanto, si desea crear un caso de prueba manualmente, debe hacer lo mismo.

La respuesta de @dmvianna me acercó mucho a ser capaz de ejecutar unittest en un cuaderno jupyter (ipython), pero tenía que hacer un poco más. Si escribiera solo lo siguiente:

 class TestStringMethods(unittest.TestCase): def test_upper(self): self.assertEqual('foo'.upper(), 'FOO') def test_isupper(self): self.assertTrue('FOO'.isupper()) self.assertFalse('Foo'.isupper()) def test_split(self): s = 'hello world' self.assertEqual(s.split(), ['hello', 'world']) # check that s.split fails when the separator is not a string with self.assertRaises(TypeError): s.split(2) suite = unittest.TestLoader().loadTestsFromModule (TestStringMethods) unittest.TextTestRunner().run(suite) 

tengo


Ran 0 pruebas en 0.000s

DE ACUERDO

No está roto, pero no ejecuta ninguna prueba! Si he instanciado la clase de prueba

 suite = unittest.TestLoader().loadTestsFromModule (TestStringMethods()) 

(note los parens al final de la línea; ese es el único cambio) Tengo


ValueError Traceback (última llamada más reciente) en () —-> 1 suite = unittest.TestLoader (). LoadTestsFromModule (TestStringMethods ())

/usr/lib/python2.7/unittest/case.pyc in init (self, methodName) 189 excepto AttributeError: 190 raise ValueError (“no hay tal método de prueba en% s:% s”% -> 191 (self. class , methodName)) 192 self._testMethodDoc = testMethod. doc 193 self._cleanups = []

ValueError: no existe tal método de prueba en: runTest

La solución ahora está razonablemente clara: agregue runTest a la clase de prueba:

 class TestStringMethods(unittest.TestCase): def runTest(self): test_upper (self) test_isupper (self) test_split (self) def test_upper(self): self.assertEqual('foo'.upper(), 'FOO') def test_isupper(self): self.assertTrue('FOO'.isupper()) self.assertFalse('Foo'.isupper()) def test_split(self): s = 'hello world' self.assertEqual(s.split(), ['hello', 'world']) # check that s.split fails when the separator is not a string with self.assertRaises(TypeError): s.split(2) suite = unittest.TestLoader().loadTestsFromModule (TestStringMethods()) unittest.TextTestRunner().run(suite) 

Corrió 3 pruebas en 0.002s

DE ACUERDO

También funciona correctamente (y ejecuta 3 pruebas) si mi runTest simplemente pass es, como lo sugiere @Darren.

Esto es un poco yucchy, que requiere un poco de trabajo manual por mi parte, pero también es más explícito , y eso es una virtud de Python, ¿no es así?

No pude obtener ninguna de las técnicas mediante la llamada a unittest.main con argumentos explícitos desde aquí o desde esta pregunta relacionada No se puede ejecutar la función principal de unittest en el cuaderno ipython / jupyter para trabajar dentro de un cuaderno jupyter, pero estoy de vuelta en la carretera Tanque lleno de gas.