ruby-on-rails - run - rspec with rails
Rieles: ¿Cómo escribo las pruebas para un módulo de ruby? (5)
Me gustaría saber cómo escribir pruebas unitarias para un módulo que está mezclado en un par de clases pero no sé cómo hacerlo.
¿Pruebo los métodos de instancia escribiendo pruebas en uno de los archivos de prueba para una clase que los incluye (no parece correcto) o puede de alguna manera mantener las pruebas de los métodos incluidos en un archivo separado específico para el módulo?
La misma pregunta se aplica a los métodos de clase.
¿Debo tener un archivo de prueba separado para cada una de las clases en el módulo, como lo hacen los modelos de rieles normales, o viven en el archivo de prueba del módulo general, si es que existe?
En minitest
ya que cada prueba es explícitamente una clase, puede simplemente incluir el módulo en la prueba y probar los métodos:
class MyModuleTest < Minitest::Test
include MyModule
def my_module_method_test
# Assert my method works
end
end
En mi humilde opinión, debe hacer una cobertura de prueba funcional que cubrirá todos los usos del módulo, y luego probarlo en forma aislada en una prueba de unidad:
setup do
@object = Object.new
@object.extend(Greeter)
end
should "greet person" do
@object.stubs(:format).returns("Hello {{NAME}}")
assert_equal "Hello World", @object.greet("World")
end
should "greet person in pirate" do
@object.stubs(:format).returns("Avast {{NAME}} lad!")
assert_equal "Avast Jim lad!", @object.greet("Jim")
end
Si las pruebas de su unidad son buenas, debería poder probar la funcionalidad en los módulos en los que se mezcla.
O…
Escriba un helper de prueba, que afirme el comportamiento correcto, luego utilícelo contra cada clase en la que está mezclado. El uso sería el siguiente:
setup do
@object = FooClass.new
end
should_act_as_greeter
Si las pruebas de su unidad son buenas, esta puede ser una simple prueba de humo del comportamiento esperado, verificar el derecho de los delegados, etc.
Intento mantener mis pruebas enfocadas 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 volveré a afirmar que se realizan las devoluciones de llamada, pero puedo afirmar que la clase incluida tiene un cierto conjunto de validación con una cierta expresión regular, algo particular de esa clase, pero no reproduciendo 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. Este es un tipo de prueba de integración, pero que no repite las mismas 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
Aquí hay algo de duplicación (como lo harán la mayoría de las pruebas de integración), pero las pruebas prueban dos cosas diferentes para mí. 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.
Por lo general, probaba el módulo con el mayor aislamiento posible, esencialmente probando los métodos, con el código, los simulacros y los fragmentos necesarios para que funcione.
Probablemente también tenga pruebas para las clases en las que están incluidos los módulos. No puedo evaluar todas las clases, pero probaré lo suficiente de las clases para obtener una buena cobertura y tener una idea de los problemas que surjan. Estas pruebas no necesitan probar explícitamente el módulo, pero ciertamente probarían su uso en escenarios particulares.
Cada conjunto de pruebas tendría su propio archivo.
Use clases en línea (no estoy usando flexmock o stubba / mocha para mostrar el punto)
def test_should_callout_to_foo
m = Class.new do
include ModuleUnderTest
def foo
3
end
end.new
assert_equal 6, m.foo_multiplied_by_two
end
Cualquier biblioteca de burla / stubbing debería brindarle una manera más limpia de hacerlo. También puedes usar structs:
instance = Struct.new(:foo).new
class<<instance
include ModuleUnderTest
end
instance.foo = 4
Si tengo un módulo que se usa en muchos lugares, tengo una prueba de unidad que hace eso (desliza un objeto de prueba bajo los métodos del módulo y prueba si los métodos del módulo funcionan correctamente en ese objeto).