2010-11-04 9 views
27

¿Cómo puedo estar seguro de la orden de los métodos unittest? ¿Los prefijos alfabéticos o numéricos son los adecuados?Pruebas de prueba de unidad orden

class TestFoo(TestCase): 
    def test_1(self): 
     ... 
    def test_2(self): 
     ... 

o

class TestFoo(TestCase): 
    def test_a(self): 
     ... 
    def test_b(self): 
     ... 
+0

posible duplicado de [cambio de orden de pruebas unitarias en Python] (http://stackoverflow.com/questions/4005695/change-order-of-unit-tests-in-python) –

+1

Tenga en cuenta que el orden en el que el se ejecutarán varios casos de prueba se determina clasificando los nombres de la función de prueba con respecto al ordenamiento integrado de las cadenas. http://docs.python.org/library/unittest.html – morsik

Respuesta

42

Se puede desactivar mediante el establecimiento de sortTestMethodsUsing en Ninguno: http://docs.python.org/2/library/unittest.html#unittest.TestLoader.sortTestMethodsUsing

Para unittests puros, que la gente tiene razón; pero para pruebas de componentes y pruebas de integración ... No estoy de acuerdo en que no asumas nada sobre el estado. ¿Qué sucede si está probando el estado? Por ejemplo, su prueba valida que un servicio se inicie automáticamente después de la instalación. Si en su configuración, inicia el servicio, luego realiza la afirmación, entonces ya no está probando el estado sino que está probando la funcionalidad de "inicio del servicio".

Otro ejemplo es cuando su configuración lleva mucho tiempo o requiere mucho espacio y simplemente no es práctico ejecutar la configuración con frecuencia.

Muchos desarrolladores tienden a utilizar frameworks "unittest" para pruebas de componentes ... así que deténgase y pregúntese, estoy haciendo pruebas unitarias o pruebas de componentes.

+10

+1 por esto: "¿Qué pasa si estás probando el estado?". Ocurre con bastante frecuencia cuando se prueban métodos que hablan con un backend DB, por ejemplo. No seas dogmático, hay excepciones legítimas a la regla, por lo demás sensata, de aislar cada prueba unitaria. –

13

¿Por qué necesita orden de la prueba específica? Las pruebas deben estar aisladas y, por lo tanto, debería ser posible ejecutarlas en cualquier orden, o incluso en paralelo.

Si necesita probar algo como la cancelación de suscripción de usuario, la prueba podría crear una nueva base de datos con una suscripción de prueba y luego tratar de darse de baja. Este escenario tiene sus propios problemas, pero al final es mejor que tener pruebas que dependen el uno del otro. (Tenga en cuenta que puede factorizar el código de prueba común, para que no tenga que repetir el código de configuración DB o crear datos de prueba ad nauseam.)

+2

Puede ser difícil ejecutarlos en paralelo si acceden a una base de datos (que es principalmente el caso con django) –

+1

Exactamente. Si necesita que las pruebas se ejecuten en un orden específico, de lo contrario, fallan, no tiene pruebas independientes, por lo tanto, algunas de ellas son incompletas o erróneas. – Piskvor

+15

Cada prueba es la continuación de la anterior. Aquí hay un ejemplo simple del orden de las pruebas. probar la suscripción del usuario, probar la inhabilitación de la suscripción, probar la cancelación de suscripción de la suscripción suscrita y deshabilitada. Debo hacer todo lo probado en la prueba anterior nuevamente si las pruebas no están ordenadas.¿Es mal camino? –

9

No confíe en la orden. Si usan algún estado común como el sistema de archivos o la base de datos, entonces debe crear los métodos setUp y tearDown que hacen que su entorno se pueda verificar y luego limpiar después de que se hayan ejecutado las pruebas. Cada prueba debe suponer que el entorno es como se define en setUp, y no debe hacer más suposiciones.

6

Existen varias razones para priorizar las pruebas, una de las cuales es la productividad, que es para lo que JUnit Max está orientado. A veces es útil mantener pruebas muy lentas en su propio módulo para que pueda obtener una respuesta rápida de las pruebas que no sufren de las mismas dependencias pesadas. El pedido también es útil para rastrear fallas de pruebas que no son completamente autónomas.

+0

Totalmente de acuerdo. – Purrell

+3

Lo siento, pero tiendo a estar en desacuerdo. Las pruebas unitarias no deben depender entre sí, pero a menudo tiene mucho sentido si se ejecutan en el orden en que se especificaron. Diga, usted tiene dos funciones '' a'' y '' b'' y '' b'' utiliza '' a''. Entonces es mucho mejor si '' test_a'' se ejecuta antes de '' test_b'', porque si '' a'' contiene un error lo detectará mucho más temprano de esta manera, en lugar de tratar de encontrar el error en '' b ''. –

+0

@ElmarZander - si 'test_b' ejecuta también' a', entonces puede tener un problema con su estructura de prueba, ya que 'test_b' terminará probando no una sola unidad' b' sino dos: 'b' y' a' . Probablemente deberías burlarte del resultado de 'a' en tu' test_b' en su lugar. Pruebas unitarias ≠ pruebas de integración. – mac

12

Si usa 'nose' y escribe los casos de prueba como funciones (y no como métodos de alguna clase derivada de TestCase) 'nose' no juega con el orden, sino que usa el orden de las funciones definidas en el archivo. Para tener a mano los métodos assert_ * sin necesidad de subclasificar TestCase, generalmente uso el módulo de prueba de numpy. Ejemplo:

from numpy.testing import * 

def test_aaa(): 
    assert_equal(1, 1) 

def test_zzz(): 
    assert_equal(1, 1) 

def test_bbb(): 
    assert_equal(1, 1) 

Ejecución que, con '' nosetest vv '' da:

test_it.test_aaa ... ok 
test_it.test_zzz ... ok 
test_it.test_bbb ... ok 
---------------------------------------------------------------------- 
Ran 3 tests in 0.050s 
OK 

Nota para todos aquellos que sostienen que las pruebas unitarias no deben ser ordenados: si bien es cierto que las pruebas unitarias debe estar aislado y puede funcionar de forma independiente, sus funciones y clases generalmente no son independientes. Más bien se acumulan en otra desde funciones más simples/de bajo nivel a funciones más complejas/de alto nivel. Cuando comienzas a optimizar tus funciones de bajo nivel y te equivocas (por mi parte, lo hago con frecuencia, si no lo haces, probablemente no necesites una prueba de unidad ;-) entonces es mucho mejor para diagnosticar la causa, cuando las pruebas de funciones simples son lo primero, y las pruebas de las funciones que dependen de esas funciones más adelante. Si las pruebas se ordenan alfabéticamente, la causa real generalmente se ahoga entre cien afirmaciones fallidas, que no están ahí porque la función bajo prueba tiene un error, sino porque tiene la función de bajo nivel en la que se basa.

Es por eso que quiero que mis pruebas unitarias sean ordenadas de la manera que las especifiqué: no usar el estado que se creó en pruebas tempranas en pruebas posteriores, sino como una herramienta muy útil para diagnosticar problemas.

+1

Tengo un conjunto de cientos de casos de prueba y, lamentablemente, no puedo decir que sea cierto. Tampoco se evita a propósito, a veces fue en este orden. Además, no estoy seguro de si es configurable en la nariz en algún lugar, pero desplazándome por la ayuda tampoco puedo distinguir la opción. – erikbwork

+0

Su ejemplo funciona, pero esto no funciona en mi caso ya que las pruebas todavía se ejecutan alfabéticamente, pero leyendo las otras respuestas, me di cuenta de que tengo que aislar mis pruebas correctamente – danidee

1

Véase el ejemplo de WidgetTestCase en http://docs.python.org/library/unittest.html#organizing-test-code, se dice que

instancias de clases serán ahora cada una de ejecutar la prueba _ *() métodos, con self.widget creado y destruido por separado para cada instancia.

De modo que puede no ser útil especificar el orden de los casos de prueba, si no se accede a las variables globales.

4

Existen situaciones en las que el orden puede ser importante y en las que la configuración y el desmontaje son limitados. Solo hay un método setUp y tearDown, lo cual es lógico, pero solo puede poner tanta información en ellos hasta que no quede claro qué setUp o tearDown realmente podrían estar haciendo.

tomar esta prueba la integración como un ejemplo:

Estás escribiendo pruebas para ver si el formulario de inscripción y el formulario de acceso están funcionando correctamente. En tal caso, el orden es importante, ya que no puede iniciar sesión sin una cuenta existente. Más importante aún, el orden de sus pruebas representa algún tipo de interacción del usuario. Donde cada prueba puede representar un paso en todo el proceso o flujo que está probando.

Dividir su código en esas piezas lógicas tiene varias ventajas.

Puede que no sea la mejor solución, pero a menudo uso un método que inicia las pruebas reales.

def test_registration_login_flow(self): 
    _test_registration_flow() 
    _test_login_flow() 
7

Estoy de acuerdo con la idea de que las pruebas no se pueden pedir. En algunos casos ayuda (¡es más fácil!) Tenerlos en orden ... después de todo, esa es la razón de la 'unidad' en UnitTest.

Dicho esto, una alternativa es usar objetos de simulación para crear fallas y aplicar parches a los elementos que deben ejecutarse antes de ese código específico bajo prueba. También puedes poner una función ficticia para parchear tu código. Para obtener más información, consulte Mock, que ahora forma parte de la biblioteca estándar. Mock

Aquí hay algunos videos de YouTube si no ha utilizado antes Mock.

Video 1

Video 2

Video 3

Más al punto, trate de usar métodos de la clase a la estructura de su código, a continuación, colocar todos los métodos de la clase en un método de ensayo principal.

import unittest 
import sqlite3 

class MyOrderedTest(unittest.TestCase): 

    @classmethod 
    def setUpClass(cls): 
     cls.create_db() 
     cls.setup_draft() 
     cls.draft_one() 
     cls.draft_two() 
     cls.draft_three() 

    @classmethod 
    def create_db(cls): 
     cls.conn = sqlite3.connect(":memory:") 

    @classmethod 
    def setup_draft(cls): 
     cls.conn.execute("CREATE TABLE players ('draftid' INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 'first', 'last')") 

    @classmethod 
    def draft_one(cls): 
     player = ("Hakeem", "Olajuwon") 
     cls.conn.execute("INSERT INTO players (first, last) VALUES (?, ?)", player) 

    @classmethod 
    def draft_two(cls): 
     player = ("Sam", "Bowie") 
     cls.conn.execute("INSERT INTO players (first, last) VALUES (?, ?)", player) 

    @classmethod 
    def draft_three(cls): 
     player = ("Michael", "Jordan") 
     cls.conn.execute("INSERT INTO players (first, last) VALUES (?, ?)", player) 

    def test_unordered_one(self): 
     cur = self.conn.execute("SELECT * from players") 
     draft = [(1, u'Hakeem', u'Olajuwon'), (2, u'Sam', u'Bowie'), (3, u'Michael', u'Jordan')] 
     query = cur.fetchall() 
     print query 
     self.assertListEqual(query, draft) 

    def test_unordered_two(self): 
     cur = self.conn.execute("SELECT first, last FROM players WHERE draftid=3") 
     result = cur.fetchone() 
     third = " ".join(result) 
     print third 
     self.assertEqual(third, "Michael Jordan") 
56

No hay razón teniendo en cuenta que no se puede construir sobre lo que se hizo en una prueba anterior o debe reconstruir todo desde cero para la próxima prueba. Al menos no se ofrece ninguna razón, pero la gente simplemente dice "no deberías". Eso no es útil.

En general, estoy cansado de leer demasiadas respuestas aquí que dicen básicamente "no deberías hacer eso" en lugar de dar ninguna información sobre cómo hacerlo mejor si a juicio del interrogador hay una buena razón para hacerlo. Si quisiera la opinión de alguien sobre si debería hacer algo, entonces habría pedido opiniones sobre si hacerlo es una buena idea.

Que dicho sea de paso, si lee loadTestsFromTestCase y lo que llama, en última instancia busca métodos con algún patrón de nombre en cualquier orden que se encuentren en el diccionario de métodos de clase, básicamente en orden de clave.Toma esta información y crea una suite de pruebas para asignarla a la clase TestCase. En su lugar, una lista ordenada como desearía es una forma de hacerlo. No estoy tan seguro de la forma más eficiente/limpia de hacerlo, pero esto funciona.

+3

Estoy de acuerdo con sus comentarios sobre "don" inútil Haga eso "comentarios sin explicaciones, pero habiendo dicho que * hay * razones genuinas por las cuales no es una buena idea tener dependencias entre las pruebas. El principal de ellos es que es bueno que las pruebas fracasen porque algo en particular se ha roto y no porque exista un vínculo poco claro e indocumentado entre la prueba que está ejecutando y alguna otra prueba que no es. Si nunca realizas pruebas aisladas, está bien, pero poder realizar pruebas individuales es útil en algunas circunstancias, y esto no es posible donde dependen el uno del otro. – JimmidyJoo

+0

La respuesta es que las pruebas unitarias deben ser independientes entre sí para que pueda ejecutarlas y depurarlas de forma aislada. – JeremyP

+8

Las pruebas unitarias deben ser independientes, verdaderas. O mejor dicho, deberían poder funcionar independientemente por muchas buenas razones. Pero también escribo pruebas funcionales, pruebas de integración y pruebas del sistema con el framework unittest, y sería inviable ejecutarlos sin ordenarlos, ya que el estado del sistema es MALTO en las pruebas de integración. –

0

Al contrario de lo que se ha dicho aquí: - pruebas tienen que funcionar de manera aislada (orden no debe asuntos para que ) Y - ordenándoles que es importante porque describen lo que el sistema de hacer y cómo el developper implementa.

IOW, cada prueba trae información del sistema y la lógica del desarrollador.

Por lo tanto, si estas informaciones no están ordenadas, pueden hacer que su código sea difícil de entender.

+0

Existen algunos escenarios en los que las pruebas * necesitan * ejecutarse en un orden específico. Por ejemplo: tengo un contenedor de API que inicia sesión en un servidor externo. El inicio de sesión * necesita * antes de cualquier otra prueba unitaria. –

+0

@StevenVascellaro Las pruebas que describe no son pruebas unitarias, el tema es sobre pruebas unitarias y NUNCA tiene un escenario donde las pruebas deben ejecutarse en un orden específico. Es un olor a código sobre el mal diseño o las pruebas incorrectas. Si escribe este tipo de pruebas, será mejor que revise lo que sabe sobre las pruebas, porque para mí las pruebas que describe son inútiles y hacen que el código sea difícil de cambiar. Piénselo, está hablando para probar un sistema externo, que ya debería probarse. Concéntrese en SU ​​sistema que es lo que está probando. – gregorySalvan

+0

El tema no es sobre pruebas unitarias sino sobre el uso de unittest para automatizar las pruebas de integración. Como ingeniero de QA Test Automation, mi trabajo es realizar pruebas de integración y basadas en navegador 'extremo a extremo'. Las pruebas unitarias (cualquiera que sea la herramienta) se encuentran en el dominio del desarrollador que realiza el primer desarrollo de prueba o que hace todo lo posible para entregar el código limpio al siguiente paso en las pruebas. – pmneve

6

Ok, puede ser un poco más tarde, pero de todos modos ...

Usted debe tratar proboscis biblioteca. Le permitirá hacer pedidos de pruebas y establecer dependencias de prueba. Lo uso y esta biblioteca es realmente increíble.

Por ejemplo, si test case #1 de module A debe depender de test case #3 de module B que CAN establecer este comportamiento usando la biblioteca.

3

http://docs.python.org/library/unittest.html

Tenga en cuenta que el orden en que se ejecutarán los distintos casos de prueba se determina por la clasificación de los nombres de las funciones de prueba con respecto a la incorporada en el pedido para cuerdas.

Si necesita establecer el orden explícitamente, utilice una prueba monolítica.

class Monolithic(TestCase): 
    def step1(self): 
     ... 

    def step2(self): 
     ... 

    def steps(self): 
    for name in sorted(dir(self)): 
     if name.startswith("step"): 
     yield name, getattr(self, name) 

    def test_steps(self): 
    for name, step in self.steps(): 
     try: 
     step() 
     except Exception as e: 
     self.fail("{} failed ({}: {})".format(step, type(e), e) 

Consulte post para obtener más detalles.

1

Estoy de acuerdo con la afirmación de que una respuesta general de "no hacer eso" es una mala respuesta.

Tengo una situación similar en la que tengo una única fuente de datos y una prueba borrará el conjunto de datos, lo que ocasionará que otras pruebas fallen.

Mi solución fue utilizar las variables de entorno del sistema operativo en mi servidor de bambú ...

(1) La prueba de la funcionalidad "datos de purga" comienza con un bucle while que comprueba el estado de una variable de entorno "BLOCK_DATA_PURGE". Si la variable "BLOCK_DATA_PURGE" es mayor que cero, el ciclo escribirá una entrada de registro con el efecto de que duerme 1 segundo. Una vez que el "BLOCK_DATA_PURGE" tiene un valor cero, la ejecución procede a probar la funcionalidad de purga.

(2) Cualquier prueba unitaria que necesite los datos en la tabla simplemente incrementa "BLOCK_DATA_PURGE" al comienzo (en setup()) y disminuye la misma variable en el desmontaje().

El efecto de esto es permitir que varios consumidores de datos bloqueen la funcionalidad de purga siempre que lo necesiten sin temor a que la purga se pueda ejecutar entre pruebas. Efectivamente, la operación de purga se lleva al último paso ... o al menos al último paso que requiere el conjunto de datos original.

Hoy voy a extender esto para agregar más funcionalidad para permitir algunas pruebas a REQUIRE_DATA_PURGE. Éstos invertirán efectivamente el proceso anterior para garantizar que esas pruebas solo se ejecuten después de la purga de datos para probar la restauración de datos.

Happy Coding!

--Sam Caldwell

+0

Felicitaciones por el primer párrafo. "No hagas eso" siempre proviene de algún tipo de inexperiencia, es decir, un desarrollador que nunca tuvo que automatizar la integración o las pruebas de aceptación del usuario. – pmneve

3

Un método simple para ordenar pruebas "unittest" es seguir el mecanismo de init.d de darles nombres numéricos:

def test_00_createEmptyObject(self): 
    obj = MyObject() 
    self.assertIsEqual(obj.property1, 0) 
    self.assertIsEqual(obj.dict1, {}) 

def test_01_createObject(self): 
    obj = MyObject(property1="hello", dict1={"pizza":"pepperoni"}) 
    self.assertIsEqual(obj.property1, "hello") 
    self.assertIsDictEqual(obj.dict1, {"pizza":"pepperoni"}) 

def test_10_reverseProperty(self): 
    obj = MyObject(property1="world") 
    obj.reverseProperty1() 
    self.assertIsEqual(obj.property1, "dlrow") 

Sin embargo, en tales casos, es posible desee considerar la estructuración de sus pruebas de manera diferente para que pueda aprovechar los casos de construcción anteriores. Por ejemplo, en lo anterior, podría tener sentido tener una función "construir y veirfy" que construya el objeto y valide su asignación de parámetros.

def make_myobject(self, property1, dict1): # Must be specified by caller 
    obj = MyObject(property1=property1, dict1=dict1) 
    if property1: 
     self.assertEqual(obj.property1, property1) 
    else: 
     self.assertEqual(obj.property1, 0) 
    if dict1: 
     self.assertDictEqual(obj.dict1, dict1) 
    else: 
     self.assertEqual(obj.dict1, {}) 
    return obj 

def test_00_createEmptyObject(self): 
    obj = self.make_object(None, None) 

def test_01_createObject(self): 
    obj = self.make_object("hello", {"pizza":"pepperoni"}) 

def test_10_reverseProperty(self): 
    obj = self.make_object("world", None) 
    obj.reverseProperty() 
    self.assertEqual(obj.property1, "dlrow") 
0

La filosofía detrás de las pruebas unitarias es hacer que sean independientes entre sí. Esto significa que el primer paso de cada prueba será tratar de volver a pensar cómo está probando cada pieza para que coincida con esa filosofía. Esto puede implicar cambiar la forma en que enfoca las pruebas y ser creativo estrechando sus pruebas a ámbitos más pequeños.

Sin embargo, si aún encuentra que necesita pruebas en un orden específico (como es viable), puede intentar consultar la respuesta al Python unittest.TestCase execution order.