with spec run rails describe ruby-on-rails ruby unit-testing testing module

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.

  1. ¿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?

  2. La misma pregunta se aplica a los métodos de clase.

  3. ¿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).