我希望知道如何为混入到几个类中的模块编写单元测试,但不太清楚该如何做:
我应该在包含这些方法的类的测试文件中编写测试来测试实例方法吗(似乎不正确),还是可以将包含方法的测试保留在特定于模块的单独文件中?
同样的问题也适用于类方法。
我是否应该为模块中的每个类都拥有一个单独的测试文件,就像普通的 Rails 模型一样,还是它们属于通用模块测试文件中(如果存在)?
我希望知道如何为混入到几个类中的模块编写单元测试,但不太清楚该如何做:
我应该在包含这些方法的类的测试文件中编写测试来测试实例方法吗(似乎不正确),还是可以将包含方法的测试保留在特定于模块的单独文件中?
同样的问题也适用于类方法。
我是否应该为模块中的每个类都拥有一个单独的测试文件,就像普通的 Rails 模型一样,还是它们属于通用模块测试文件中(如果存在)?
我认为你应该进行功能测试覆盖,以覆盖模块的所有用途,并在单元测试中对其进行隔离测试:
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
如果您的单元测试很好,这可以是对预期行为的简单烟雾测试,检查正确的委托是否被调用等。
使用内联类(我没有使用任何复杂的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
如果我有一个被多处使用的模块,针对这个模块我会编写一个单元测试,测试对象会在模块方法下滑动并检查这些方法是否正确地处理了该对象。
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
我试图让我的测试只关注该特定类/模块的合同。如果我已经在该模块的测试类中证明了其行为(通常是通过在该模块规范中声明的测试类中包含该模块),那么我就不会为使用该模块的生产类复制该测试。但是,如果有其他行为需要测试生产类或集成方面的问题,我将为生产类编写测试。
例如,我有一个名为 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
在minitest
中,由于每个测试都是显式的类,您只需将模块包含到测试中并测试方法:
class MyModuleTest < Minitest::Test
include MyModule
def my_module_method_test
# Assert my method works
end
end
我通常会尽可能地将模块隔离开来进行测试,基本上只测试方法,使用足够的代码、模拟和存根来使其工作。
然后,我可能也会对包含该模块的类进行测试。我可能不会测试每个类,但会测试足够的类以获得良好的覆盖率并了解任何出现的问题。这些测试不需要显式地测试模块,但肯定会测试其在特定场景下的使用情况。
每组测试都有自己的文件。