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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2146040/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 22:12:44  来源:igfitidea点击:

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

ruby-on-railsrubyunit-testingtestingmodule

提问by tsdbrown

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 instance methods by writing tests in one of the test files for a class that includes them (doesn't seem right) or can you somehow keep the tests for the included methods in a separate file specific to the module?

  2. The same question applies to the class methods.

  3. Should I have a separate test file for each of the classes in the module like normal rails models do, or do they live in the general module test file, if that exists?

  1. 我是否通过在包含它们的类的测试文件之一中编写测试来测试实例方法(似乎不正确),或者您能否以某种方式将包含方法的测试保存在特定于模块的单独文件中?

  2. 同样的问题也适用于类方法。

  3. 我是否应该像普通 Rails 模型一样为模块中的每个类创建一个单独的测试文件,或者它们是否存在于通用模块测试文件中(如果存在)?

回答by cwninja

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.

如果您的单元测试良好,这可以是对预期行为的简单冒烟测试,检查是否调用了正确的委托等。

回答by Julik

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

使用内联类(我没有做任何花哨的 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

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

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

回答by lcguida

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

minitest因为每个测试是明确的一类你可以只包括该模块的测试和测试方法:

class MyModuleTest < Minitest::Test
   include MyModule

   def my_module_method_test
     # Assert my method works
   end
end

回答by Dave Sims

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 AttributeValidatorthat performs lightweight validations kind of similar to ActiveRecord. I write tests for the module's behavior in the module spec:

例如,我有一个名为的模块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

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.

希望有帮助。

回答by Toby Hede

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.

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

回答by Marnen Laibow-Koser

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