Sin categoría

Cómo probarlo todo

[Este post también aparece en Blog de Dustin en github].

Hace poco tuve un Membase usuario señalar una secuencia de operaciones que condujo a un estado indeseable. Tengo un montón de pruebas de motor muy buenas que he escrito, pero no este caso:

añadir con timeout -> esperar timeout -> añadir con timeout

El fallo es bastante sencillo - la caducidad es perezosa y resulta que no estoy comprobando la caducidad en este caso. Era bastante fácil

escribir esta prueba, pero inmediatamente me hizo pensar en lo que otros no se llevaban casos.

Sé que existen innumerables herramientas para ayudar en las pruebas. He escrito otra. Probablemente pasé una hora o así escribiendo un marco para escribir y ejecutar todas las pruebas que necesitaba. La diferencia entre lo que estoy describiendo aquí y, por ejemplo, comprobación rápida es que quiero algo muy simple para expresar acciones que esperan que su entorno esté en un estado particular y dejarán el entorno en otro estado. Entonces quiero golpear a todas las posibles disposiciones de estas acciones para asegurarse de que no interfieran entre sí de manera inesperada.

Permutations

Esto se dispara muy rápidamente, concretamente el número de pruebas generadas para una secuencia de pruebas de n acciones de a acciones posibles es de aproximadamente an.

Consideremos tres acciones definidas permutadas en secuencias de dos. Esto da lugar a nueve posibilidades, como se muestra en el diagrama de la derecha.

Las acciones del diagrama se definen con semántica memcached sobre una única clave, por lo que añada tiene como requisito previo que el elemento no debe existen y del tiene como requisito previo que el elemento debe existe.

La prueba generada espera el éxito en cada casilla blanca, el fracaso en cada casilla roja, y rastrea las mutaciones de estado esperadas para construir aserciones.

BreakDancer Growth

Mi primera prueba ... um, prueba corrió con 11 acciones en secuencias de 4 acciones. Me faltan más acciones, pero 4 es una longitud bastante buena, por lo que el gráfico de la izquierda va a demostrar mi tasa de crecimiento.

Lo mejor es que ha detectado el error original con bastante facilidad y otro par de errores con un esfuerzo limitado.

¿Cómo se utiliza?

action lifecycle

La API es hasta ahora bastante simple y componible. Básicamente hay cinco clases (tres se muestran en la imagen de la derecha).

Condición

A Condición es un callable simple que se utiliza para precondiciones y postcondiciones. A una clase dada no le importa para cuál se utiliza, y en muchos casos se utilizará para ambas.

Por ejemplo, considere mi implementación de NoExiste:

clase NoExiste(Condición):
def __call__(auto, estado):
devolver TESTKEY no en estado

Efecto

En Efecto cambia nuestra visión del estado (y dependiendo del controlador, puede hacer que algo en el mundo cambie con él). Por ejemplo, el controlador StoreEffect funciona de la siguiente manera:

clase StoreEffect(Efecto):
def __call__(auto, estado):
estado[TESTKEY] = ‘0’

Acción

En Acción aporta un Efecto y uno o varios Condición como condiciones previas y posteriores. Por ejemplo, veremos dos acciones, una Añadir acción y una Establecer acción:

clase Conjunto(Acción):
efecto = StoreEffect()
postconditions = [Exists()]

clase Añadir(Acción):
preconditions = [DoesNotExist()]
efecto = StoreEffect()
postconditions = [Exists()]

Lo interesante de esto es que Set y Add tienen una semántica diferente, pero se expresan como composiciones diferentes de las mismas condiciones y efectos.

Conductor

Driver es algo más grande (¡siete métodos definidos!). Hace lo suficiente para que pueda hacer cualquier cosa, desde generar un conjunto de pruebas en C para motores memcached hasta ejecutar pruebas a través de un protocolo remoto.

No voy a describir todo el asunto aquí, ya que está documentado en el fuente. Sin embargo, voy a cerrar el círculo mostrando un código de ejemplo que generó y que demostró el error que no pudimos encontrar en primer lugar:

static enum test_result test_add_add_delay_add(ENGINE_HANDLE *h,
ENGINE_HANDLE_V1 *h1) {
añada(h, h1);
assertHasNoError(); // valor es “0”
añada(h, h1);
assertHasError(); // valor es “0”
retraso(caducidad+<span clase="mi">1);
assertHasNoError(); // valor es no definido
añada(h, h1);
assertHasNoError(); // valor es “0”
checkValue(h, h1, “0”);
devolver ÉXITO;
}
</span>

Eso demuestra cuánta información sabes en cada paso del camino. A partir de ahí, podemos hacer todo tipo de cosas con nuestros stubs (retraso anterior se implementa con el memcached testapp "viaje en el tiempo" característica, por ejemplo).

A partir de aquí, es menos emocionante. Proporcionamos restricciones, escribe pruebas, y se asegura de que hay otra área que es imposible que los usuarios se encuentran con algo que no hemos visto antes.

Comparte este artículo
Recibe actualizaciones del blog de Couchbase en tu bandeja de entrada
Este campo es obligatorio.

Autor

Publicado por Dustin Sallings

Dustin Sallings es Arquitecto Jefe en Couchbase. Dustin es autor de spymemcached y colaborador principal de Couchbase y <a href="https://developer.couchbase.com/documentation/server/3.x/developer/dev-guide-3.0/memcached.html#projects">Proyectos Memcached.</a>

Deja un comentario

¿Listo para empezar con Couchbase Capella?

Empezar a construir

Consulte nuestro portal para desarrolladores para explorar NoSQL, buscar recursos y empezar con tutoriales.

Utilizar Capella gratis

Ponte manos a la obra con Couchbase en unos pocos clics. Capella DBaaS es la forma más fácil y rápida de empezar.

Póngase en contacto

¿Quieres saber más sobre las ofertas de Couchbase? Permítanos ayudarle.