2010-01-19 7 views
17

Tengo un caso de prueba:ValueError: No existe el método de prueba en <clase 'myapp.tests.SessionTestCase'>: runTest

class LoginTestCase(unittest.TestCase): 
    ... 

me gustaría utilizarlo 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 unittest donde se llama la excepción, y parece que las pruebas no son s propuesto para ser escrito de esta manera. ¿Hay alguna manera estándar de escribir algo que te gustaría probar para que pueda ser reutilizado en exámenes posteriores? ¿O hay una solución alternativa?

He añadido un método runTest vacío al LoginTest como una solución dudosa por el momento.

Respuesta

-4

unittest hace magia negro profundo - si decide usarlo para ejecutar sus pruebas unitarias (lo hago, ya que de esta manera puedo usar una batería muy potente de corredores de prueba & c integrado en el sistema de compilación en mi lugar de trabajo , pero definitivamente hay alternativas que valen la pena), será mejor que sigas sus reglas.

En este caso, simplemente tendría EditProfileTestCase derivado de LoginTestCase (en lugar de directamente de unittest.TestCase). Si hay algunas partes de LoginTestCase que también desea probar en el entorno diferente de EditProfileTestCase, y otras que no, es una simple cuestión de refactorizar LoginTestCase en esas dos partes (posiblemente usando herencia múltiple) y si algunas cosas debe suceder de manera ligeramente diferente en los dos casos, factorizarlos en "métodos de gancho" auxiliares (en un patrón de diseño de "Método de plantilla") - Utilizo todos estos enfoques a menudo para disminuir la repetición y aumentar la reutilización en las pruebas de unidades abundantes I siempre escriba (si tengo una cobertura de prueba unitaria de < 95%, siempre me siento realmente incómodo; debajo del 90%, comienzo a sentirme físicamente enfermo ;-).

+18

¿Cómo responde la pregunta? ¿Preguntó sobre la virtud de una buena cobertura? ¿Por qué no dicen sobre cómo "jugar según sus reglas"? "¡magia negra profunda" de verdad !? – saaj

+5

Desplácese hacia abajo, esta es la respuesta menos útil con diferencia. – jwg

25

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

lo que no hay "runTest" en esa clase y todas las pruebas funciones están siendo llamados. Sin embargo, si observas la clase base "TestCase" (lib/python/unittest/case.py), entonces encontrarás 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 de que unittest.main funciona bien se basa en el hecho de que no necesita 'runTest' - se puede imitar el comportamiento creando una instancia de la subclase TestCase-para todos los métodos que usted tiene en la subclase - acaba de proporcionar el nombre como primer argumento: respuesta

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

Es posible que desee comprobar para asegurarse de que 'method' es realmente una función dentro del bucle' for', una propiedad como 'test_name =" foo "' puede ser un falso positivo aquí – benjaminz

4

de Guido es casi allí, sin embargo, no explica la cosa. Necesitaba ver el código unittest para captar el flujo.

Digamos que tiene lo siguiente.

import unittest 

class MyTestCase(unittest.TestCase): 

    def testA(self): 
    pass 

    def testB(self): 
    pass 

Cuando se utiliza unittest.main(), tratará de descubrir los 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 éste hace, es convertir la clase de casos de prueba en banco de pruebas, que contiene las instancias de la clase por su método de ensayo. 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.

21

He aquí algunos 'profunda magia negro':

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

muy útil si lo que desea es probar ejecutar pruebas unitarias de una concha (es decir, IPython).

+2

Además de lo que sucede, esa es la solución real para el problema que tuve! ¡No estoy seguro de por qué está "abajo" en lugar de arriba! Gracias – Davide

6

Si no te importa la edición de código de unidad de módulo de prueba, la solución simple es añadir bajo case.py clase TestCase un nuevo método llamado runTest que no hace nada.

El archivo para editar se encuentra bajo pythoninstall \ Lib \ unittest \ case.py

def runTest(self): 
    pass 

Esto evitará que alguna vez conseguir este error.

+3

Esto también funciona agregando estas líneas de código directamente en su sub-clase 'unittest.TestCase' – tuned

2

@ La respuesta de dmvianna me dio la posibilidad de ejecutar unittest en un cuaderno jupyter (ipython), pero tuve que hacer un poco más. Si escribiera simplemente 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 tests in 0.000s

OK

No está rota, pero no se ejecuta ninguna prueba! Si crea una instancia de la clase de prueba

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

(tenga en cuenta los parens al final de la línea, que es el único cambio) Tengo


ValueError Traceback (most recent call last) in() ----> 1 suite = unittest.TestLoader().loadTestsFromModule (TestStringMethods())

/usr/lib/python2.7/unittest/case.pyc in init(self, methodName) 189 except AttributeError: 190 raise ValueError("no such test method in %s: %s" % --> 191 (self.class, methodName)) 192 self._testMethodDoc = testMethod.doc 193 self._cleanups = []

ValueError: no such test method in : runTest

La solución es ahora bastante claro: poner runTest a la prueba clase:

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) 

Ran 3 tests in 0.002s

OK

también funciona correctamente (y corre 3 pruebas) si mi runTest solo pass es, según lo sugerido por @Darren.

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

No pude obtener ninguna de las técnicas llamando al unittest.main con argumentos explícitos desde aquí o desde esta pregunta relacionada Unable to run unittest's main function in ipython/jupyter notebook para trabajar dentro de un portátil Jupyter, pero estoy de regreso en la carretera con un tanque lleno de gasolina.

+0

en el método' runTest', ¿quiso decir 'self.test_upper()' en lugar de 'test_upper (self)' ? – benjaminz

Cuestiones relacionadas