Intento mantener mis pruebas centradas solo en el contrato para esa clase/módulo en particular. Si compruebo el comportamiento del módulo en una clase de prueba para ese módulo (generalmente incluyendo ese módulo en una clase de prueba declarada en la especificación para ese módulo), entonces no duplicaré esa prueba para una clase de producción que use ese módulo. Pero si hay un comportamiento adicional que deseo probar para la clase de producción, o cuestiones de integración, escribiré pruebas para la clase de producción.
Por ejemplo, tengo un módulo llamado AttributeValidator
que realiza validaciones livianas similares a ActiveRecord
. Escribo pruebas para el comportamiento del módulo en la especificación del módulo:
before(:each) do
@attribute_validator = TestAttributeValidator.new
end
describe "after set callbacks" do
it "should be invoked when an attribute is set" do
def @attribute_validator.after_set_attribute_one; end
@attribute_validator.should_receive(:after_set_attribute_one).once
@attribute_validator.attribute_one = "asdf"
end
end
class TestAttributeValidator
include AttributeValidator
validating_str_accessor [:attribute_one, /\d{2,5}/]
end
Ahora en una clase de producción que incluye el módulo, no voy a volver a afirmar que las devoluciones de llamada se realizan, pero puedo afirmar que la clase incluido tiene un cierto conjunto de validación con cierta expresión regular, algo particular para esa clase, pero no reproduce las pruebas que escribí para el módulo.En la especificación para la clase de producción, quiero garantizar que se establezcan validaciones particulares, pero no que las validaciones funcionen en general. Esta es una especie de prueba de integración, pero que no repita los mismos afirmaciones que hice para el módulo:
describe "ProductionClass validation" do
it "should return true if the attribute is valid" do
@production_class.attribute = @valid_attribute
@production_class.is_valid?.should be_true
end
it "should return false if the attribute is invalid" do
@production_class.attribute = @invalid_attribute
@production_class.is valid?.should be_false
end
end
Hay alguna duplicación aquí (como la mayoría de las pruebas de integración tendrán), pero las pruebas demuestran dos cosas diferentes para mi Un conjunto de pruebas comprueba el comportamiento general del módulo, el otro demuestra las preocupaciones particulares de implementación de una clase de producción que usa ese módulo. A partir de estas pruebas, sé que el módulo validará atributos y realizará devoluciones de llamadas, y sé que mi clase de producción tiene un conjunto específico de validaciones para criterios específicos exclusivos de la clase de producción.
Espero que ayude.
Cuando dice "cobertura de prueba funcional", supongo que se refiere a la funcionalidad que adquieren los modelos y no a las pruebas de controlador almacenadas prueba/funcional. Gracias por su respuesta. Me gusta la idea de probar el módulo de forma aislada y escribir un ayudante que las otras clases puedan llamar para usar ese módulo. – tsdbrown
Por funcional quiero decir desde el exterior hacia adentro. Esta suele ser una prueba de controlador, pero no siempre. De cualquier forma, la cobertura funcional debe tocar (o al menos pastar) todas las áreas del sistema. Si las pruebas de su unidad son fuertes, las pruebas funcionales a menudo son suficientes para cubrir su culo. Escribir demasiadas pruebas de bajo nivel puede ser una mala inversión. Si nunca va a fallar solo, ¿atrapa errores? ¿Se ha guardado el "tiempo probable de depuración" * "probabilidad de un error"> "tiempo para escribir la prueba"? Ignora esto si un error puede matar a personas o a tu empresa. –
cwninja
No. Las pruebas de controlador son (casi) siempre malas ideas (las historias de Pepino hacen lo mismo mejor), y no son relevantes para el tema en cuestión de todos modos. Solo prueba de unidad como en la primera muestra de código. –