Kyl*_*tan 2 ruby testing rspec metaprogramming
这是一些简单的代码,对于指定的每个参数,将添加以该参数命名的特定get/set方法.如果你写attr_option :foo, :bar,那么你将看到#foo/foo=和#bar/bar=实例方法Config:
module Configurator
class Config
def initialize()
@options = {}
end
def self.attr_option(*args)
args.each do |a|
if not self.method_defined?(a)
define_method "#{a}" do
@options[:"#{a}"] ||= {}
end
define_method "#{a}=" do |v|
@options[:"#{a}"] = v
end
else
throw Exception.new("already have attr_option for #{a}")
end
end
end
end
end
Run Code Online (Sandbox Code Playgroud)
到现在为止还挺好.我想写一些RSpec测试来验证这段代码实际上正在做它应该做的事情.但是有一个问题!如果我attr_option :foo在其中一个测试方法中调用,那么现在在Config中永远定义了该方法.因此,后续测试将失败,因为foo已经定义:
it "should support a specified option" do
c = Configurator::Config
c.attr_option :foo
# ...
end
it "should support multiple options" do
c = Configurator::Config
c.attr_option :foo, :bar, :baz # Error! :foo already defined
# by a previous test.
# ...
end
Run Code Online (Sandbox Code Playgroud)
有没有办法我可以给每个测试一个Config独立于其他测试的匿名"克隆" 类?
"克隆" Config类的一种非常简单的方法是简单地使用匿名类对其进行子类化:
c = Class.new Configurator::Config
c.attr_option :foo
d = Class.new Configurator::Config
d.attr_option :foo, :bar
Run Code Online (Sandbox Code Playgroud)
这对我来说没有错误.这是有效的,因为所有设置的实例变量和方法都与匿名类相关联而不是Configurator::Config.
语法Class.new Foo创建一个带有Foo超类的匿名类.
此外,throw荷兰国际集团的ExceptionRuby中是不正确; Exceptions是raised. throw意思是像a一样使用goto,例如打破多个巢.阅读此Ruby编程部分,以获得有关差异的详细说明.
作为另一种风格的挑剔,尽量不要if not ...在Ruby中使用.这unless是为了什么.但除非 - 否则也是糟糕的风格.我将args.each块重写为:
raise "already have attr_option for #{a}" if self.method_defined?(a)
define_method "#{a}" do
@options[:"#{a}"] ||= {}
end
define_method "#{a}=" do |v|
@options[:"#{a}"] = v
end
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1120 次 |
| 最近记录: |