Debería ver el problema desde una perspectiva diferente. La prueba de si una función utiliza una transacción es inútil desde un punto de vista de comportamiento. No proporciona información sobre si la función SE ACONSEJA como se esperaba.
Lo que debe probar es el comportamiento, es decir, el resultado esperado es correcto. Para mayor claridad, digamos que ejecuta la operación A y la operación B dentro de la función (ejecutada dentro de una transacción). La Operación A acredita a un usuario 100 USD en su aplicación. La Operación B carga la tarjeta de crédito de los usuarios con 100 USD.
Ahora debe proporcionar información de entrada no válida para la prueba, de modo que el débito de la tarjeta de crédito del usuario falla. Envuelva toda la llamada a la función en un expect { ... }.not_to change(User, :balance)
.
De esta forma, prueba el COMPORTAMIENTO esperado: si el débito de la tarjeta de crédito falla, no acredite al usuario el importe. Además, si solo refactoriza su código (p.deja de usar transacciones y deshacer cosas manualmente), entonces el resultado de su caso de prueba no debería verse afectado.
Dicho esto, aún debe probar ambas operaciones en forma aislada como se menciona en @luacassus. Además, es correcto que su caso de prueba falle en caso de que haya realizado un cambio "incompatible" (es decir, cambie el comportamiento) con el código fuente mencionado como @ rb512.
O un poco mejor 'esperar {...} .no_hacer cambios (Usuario,: saldo)' – Andrew
Gracias por el comentario @Andrew, siempre estoy atento a estos pequeños ajustes :) Lo cambié en mi respuesta. – emrass
Esta respuesta es útil en teoría, pero no hay una gran manera de probar un comportamiento de concurrencia como este. Además, suponiendo que confiemos en que la implementación de 'transacción 'de ActiveRecord es correcta, solo necesitamos probar que se le pasó el bloque en particular (no tengo una buena manera de probar esto, por cierto ...) –