Rails:如何为ruby模块编写测试?

时间:2010-01-27 10:27:49

标签: ruby-on-rails ruby unit-testing testing module

我想知道如何为一个混合成几个类的模块编写单元测试,但不太知道如何去做:

  1. 我是否通过在其中一个测试文件中为包含它们的类(似乎不对)编写测试来测试实例方法,或者可以以某种方式将包含的方法的测试保存在单独的文件中到模块?

  2. 同样的问题适用于班级方法。

  3. 我是否应该为模块中的每个类提供单独的测试文件,例如普通的rails模型,或者它们是否存在于通用模块测试文件中(如果存在)?

6 个答案:

答案 0 :(得分:57)

恕我直言,您应该进行功能测试,覆盖模块的所有用途,然后在单元测试中单独测试:

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

如果您的单元测试结果良好,您应该能够对其混合的模块中的功能进行抽烟测试。

或者...

编写一个测试助手,断言正确的行为,然后对它混入的每个类使用它。用法如下:

setup do
  @object = FooClass.new
end

should_act_as_greeter

如果你的单元测试是好的,这可以是对预期行为的简单冒烟测试,检查正确的代表被调用等。

答案 1 :(得分:13)

使用内联类(我没有做任何花哨的flexmock或stubba / mocha用法只是为了说明这一点)

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

那里的任何模拟/存根库都应该为您提供更清晰的方法。你也可以使用结构:

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

如果我有一个在很多地方使用的模块,我会对它进行单元测试(在模块方法下滑动测试对象并测试模块方法是否在该对象上正常工作)。

答案 2 :(得分:4)

我尝试将测试仅关注于特定类/模块的合同。如果我已经在该模块的测试类中证明了模块的行为(通常通过将该模块包含在该模块的规范中声明的测试类中),那么我将不会为使用该模块的生产类复制该测试。但是,如果我想为生产类或集成问题测试其他行为,我将为生产类编写测试。

例如,我有一个名为AttributeValidator的模块,它执行类似于ActiveRecord的轻量级验证。我在模块规范中编写了模块行为的测试:

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

现在在包含该模块的生产类中,我不会重新断言回调是在做的,但是我可以断言所包含的类具有某个具有某个正则表达式的验证集,这是该类特有的,但没有复制我为模块编写的测试。在生产类的规范中,我想保证设置了特定的验证,但是验证通常不起作用。这是一种集成测试,但不会重复我为模块做出的相同断言:

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

这里有一些重复(大多数集成测试会有),但测试证明了两件不同的事情。一组测试证明了模块的一般行为,另一组测试证明了使用该模块的生产类的特定实现问题。从这些测试中我知道模块将验证属性并执行回调,我知道我的生产类对生产类特有的特定条件有一组特定的验证。

希望有所帮助。

答案 3 :(得分:4)

minitest中,由于每个测试都是明确的类,因此您可以仅将模块包含在测试中并测试方法:

class MyModuleTest < Minitest::Test
   include MyModule

   def my_module_method_test
     # Assert my method works
   end
end

答案 4 :(得分:3)

我通常会尽可能多地隔离模块,基本上测试方法,只需要足够的代码,模拟和存根来使其工作。

然后我可能还会对包含模块的类进行测试。我可能不会测试每个类,但会测试足够的类以获得良好的覆盖率并深入了解出现的任何问题。这些测试不需要显式测试模块,但肯定会测试它在特定场景中的用法。

每组测试都有自己的文件。

答案 5 :(得分:1)

我想做的是创建一个新的主机类,并将模块混入其中,如下所示:

Last