Rails: How do I write tests for a ruby module?

前端 未结 6 890
春和景丽
春和景丽 2021-01-31 02:22

I would like to know how to write unit tests for a module that is mixed into a couple of classes but don\'t quite know how to go about it:

  1. Do I test the instanc

相关标签:
6条回答
  • 2021-01-31 02:27

    What I like to do is create a new host class and mix the module into it, something like this:

    describe MyModule do
      let(:host_class) { Class.new { include MyModule } }
      let(:instance) { host_class.new }
    
      describe '#instance_method' do
        it 'does something' do
          expect(instance.instance_method).to do_something
        end
      end
    end
    
    0 讨论(0)
  • 2021-01-31 02:35

    I would generally test the module in as much isolation as possible, essentially testing the methods, with just enough code, mocks and stubs to get it working.

    I would then probably also have tests for the classes the modules is included in. I may not test every class, but would test enough of the classes to get good coverage and have insight into any issues that arise. These tests don't need to explicitly test the module, but would certainly test it's usage in particular scenarios.

    Each set of tests would have its own file.

    0 讨论(0)
  • 2021-01-31 02:38

    IMHO, you should be doing functional test coverage that will cover all uses of the module, and then test it in isolation in a unit test:

    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
    

    If your unit tests are good, you should be able to just smoke test the functionality in the modules it is mixed into.

    Or…

    Write a test helper, that asserts the correct behaviour, then use that against each class it's mixed in. Usage would be as follows:

    setup do
      @object = FooClass.new
    end
    
    should_act_as_greeter
    

    If your unit tests are good, this can be a simple smoke test of the expected behavior, checking the right delegates are called etc.

    0 讨论(0)
  • 2021-01-31 02:38

    In minitest since each test is explicitly a class you can just include the module to the test and test the methods:

    class MyModuleTest < Minitest::Test
       include MyModule
    
       def my_module_method_test
         # Assert my method works
       end
    end
    
    0 讨论(0)
  • 2021-01-31 02:39

    I try to keep my tests focused only on the contract for that particular class/module. If I've proven the module's behavior in a test class for that module (usually by including that module in a test class declared in the spec for that module) then I won't duplicate that test for a production class that uses that module. But if there's additional behavior that I want to test for the production class, or integration concerns, I'll write tests for the production class.

    For instance I have a module called AttributeValidator that performs lightweight validations kind of similar to ActiveRecord. I write tests for the module's behavior in the module spec:

    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
    

    Now in a production class that includes the module, I won't re-assert that the callbacks are made, but I may assert that the included class has a certain validation set with a certain regular expression, something particular to that class, but not reproducing the tests I wrote for the module. In the spec for the production class, I want to guarantee that particular validations are set, but not that validations work in general. This is a kind of integration test, but one that doesn't repeat the same assertions I made for the module:

    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
    

    There is some duplication here (as most integration tests will have), but the tests prove two different things to me. One set of tests prove the general behavior of the module, the other proves particular implementation concerns of a production class that uses that module. From these tests I know that the module will validate attributes and perform callbacks, and I know that my production class has a specific set of validations for specific criteria unique to the production class.

    Hope that helps.

    0 讨论(0)
  • 2021-01-31 02:43

    Use inline classes (I am not doing any fancy flexmock or stubba/mocha usage just to show the point)

    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
    

    Any mocking/stubbing library out there should give you a cleaner way to do this. Also you can use structs:

     instance = Struct.new(:foo).new
     class<<instance
         include ModuleUnderTest
     end
     instance.foo = 4
    

    If I have a module that is being used in many places I have a unit test for it which does just that (slide a test object under the module methods and test if the module methods function properly on that object).

    0 讨论(0)
提交回复
热议问题