2012-03-18 13 views
5

Tiene un problema extraño con Pytest's test y PyMongo. La prueba tiene éxito o fracasa al azar:Prueba de prueba de unidad de Python MongoDB falla aleatoriamente

import unittest 
from pymongo import Connection 

from tractor import Tractor 




class TestTractor(unittest.TestCase): 
    def setUp(self): 
     self.tractor = Tractor(1) 

     self.mongo = Connection() 
     self.db = self.mongo.tractor 

     self.db.classes.remove({'name': {'$regex':'^test_'}}) 

     self.action_class_id = self.db.classes.insert({'name': 'test_action', 
                 'metaclass': 'action'}) 
     self.object_class_id = self.db.classes.insert({'name': 'test_object', 
                 'metaclass': 'object'}) 


    def tearDown(self): 
     self.tractor = None 



    def test_create_class(self): 
     cid1 = self.tractor.create_action_class('test_create_action_class') 
     cid2 = self.tractor.create_object_class('test_create_object_class') 

     self.assertNotEqual(cid1, None) 
     self.assertNotEqual(cid2, None) 

     action_obj = self.db.classes.find_one({'_id': cid1}) 
     object_obj = self.db.classes.find_one({'_id': cid2}) 

     self.assertNotEqual(cid1, cid2) 
     self.assertEqual(action_obj['_id'], cid1) 
     self.assertEqual(object_obj['_id'], cid2) 

     self.assertEqual(action_obj['name'], 'test_create_action_class') 
     self.assertEqual(object_obj['name'], 'test_create_object_class') 

Clase siendo probado:

from pymongo import Connection 
from pymongo.objectid import ObjectId 



class Tractor(object): 
    def __init__(self, uid): 
     self.uid = uid 
     self.mongo = Connection() 
     self.db = self.mongo.tractor 


    # Classes 

    def create_action_class(self, name): 
     return self.db.classes.insert({'name': name, 
             'attributes': [], 
             'metaclass': 'action'}) 

    def create_object_class(self, name): 
     return self.db.classes.insert({'name': name, 
             'attributes': [], 
             'metaclass': 'object'}) 

comportamiento aleatorio:

[email protected] ~/projects/traction/tractor $ python -m unittest discover 
......ssEssssssssss 
====================================================================== 
ERROR: test_create_class (tests.test_tractor.TestTractor) 
---------------------------------------------------------------------- 
Traceback (most recent call last): 
    File "/home/silver/projects/traction/tractor/tests/test_tractor.py", line 64, in test_create_class 
    self.assertEqual(action_obj['_id'], cid1) 
TypeError: 'NoneType' object is not subscriptable 

---------------------------------------------------------------------- 
Ran 19 tests in 0.023s 

FAILED (errors=1, skipped=12) 

...

[email protected] ~/projects/traction/tractor $ python -m unittest discover 
......ss.ssssssssss 
---------------------------------------------------------------------- 
Ran 19 tests in 0.015s 

OK (skipped=12) 

Estos dos resultados se produce al azar para la misma prueba que vuelvo a ejecutar la t est sin cambiar nada ni en la clase ni en la prueba.

Todo esto se ejecuta en mi máquina y estoy seguro de que, mientras se ejecuta la prueba, nadie más juega con MongoDB ni con el código.

¿Qué ofrece?

+0

Quizás tenga una condición de carrera. Si recibe 'None' para su búsqueda, sería aconsejable enviar esta prueba unitaria a un depurador y rastrear exactamente qué fue lo que causó su ataque. – MrGomez

+0

@MrGomez, ¿piensas que insert() regresa antes de que el ítem se inserte realmente, y un hallazgo rápido de find() no lo encuentra en ese momento? Pero luego insertar() no volvería, ¿no? – sssilver

+0

Esa es al menos mi teoría. La mejor de las suertes en su depuración. – MrGomez

Respuesta

4

Sospecho que el problema aquí es que no está utilizando el modo "seguro" para sus escrituras.

Por defecto, MongoDB usa el modo "disparar y olvidar". Esto significa que el comando de inserción se envía al servidor, pero el controlador no verifica las respuestas del servidor.

Cuando cambie al modo "seguro", el controlador enviará el comando de inserción y luego enviará un segundo comando getLastError. Este segundo comando volverá cuando el servidor realmente haya cometido la escritura.

De nuevo, de forma predeterminada, se está ejecutando en el modo "disparar y olvidar", por lo que de hecho existe una posible condición de carrera aquí. Para las pruebas unitarias, deberá ejecutar el modo "seguro".

La firma de la función para la inserción se define here. Sin embargo, también debe poder realizar el cambio en el nivel de Conexión para que cada conexión al DB use el modo "seguro" de manera predeterminada.

+0

¡¡Absolutamente !! Eso solucionó todo y resolvió todo el problema. Esto me ha causado una depuración tan difícil. – sssilver