0

我是 Ruby 编程的新手,但我想我或多或少明白了符号的意义(一个不可变的值,占用更少的空间,有点像枚举等)这就是困扰我的问题——来自 Rake 测试Hartl Ruby on Rails 教程:

describe "something" do
  let(:user) { FactoryGirl.create(:user) } 
  before { sign_in user }
...
end

这对我来说是有意义的FactoryGirl.create(:user)——这里我们将一种标志 ( :user) 传递给 FactoryGirl,以告诉它要制作什么样的对象(或者更准确地说,要设置什么变量,因为它只创建 User 对象)。但是 in let(user),user是一个变量,而不是一个符号(或者至少应该是)。我只能假设该let方法正在秘密地将符号换成:user同名变量——但为什么呢?不是let(user)更直观的语法吗?我在这里想念什么?

此外,在 Ruby 中还有其他地方出现这种模式吗?到目前为止,我只在 rake 测试中看到过它。

4

2 回答 2

2

就像 Dave 已经提到的,提供的代码不是 rake 任务,而是 RSpec 规范。

但让我在这里关注真正的问题。

如果您从未接触过具有类似功能的任何其他语言,则符号一开始有点难以掌握。一些语言称它为 Atom。

http://en.wikipedia.org/wiki/Symbol_(编程)

符号的想法是提供一种人类可读但计算成本低的原始类型。

在 Ruby 中,当编译器/解释器看到一个符号时,它会创建一个 Symbol 类型的对象并将其存储在内存中。在 ruby​​ 中,符号是单例,因此任何其他使用相同符号的方法都会返回完全相同的对象,这使得它在空间方面非常便宜,而且比较起来也非常便宜,因为您可以只比较内存地址而不是内容。

例如,如果您比较两个符号,如下所示:

:foo == :foo

您几乎是在比较同一个对象,这意味着只需要比较内存地址。

现在,当您比较两个字符串时:

"foo" == "foo"

它创建两个具有相同内容的 String 实例,并且需要比较字符串的每个字节以确保它们相等。

这个属性使得 Symbols 非常适合散列中的标识符或键。

现在,到 RSpec。

让我们看下面的例子:

describe Authenticator do
  let(:user) { Factory.create(:user) )

  it "authenticate" do
    auth_user = subject.authenticate(user.login, user.password)
    auth_user.should == user
  end
end

Factory.create 将符号作为要使用的工厂的标识符。您需要自己定义工厂,所以实际上它只是一个名称。您可以使用字符串,但使用符号更便宜也是最佳实践,但老实说,除非您调用 Factory.create 数百万次,否则不会有太大区别。

let 不是定义一个变量,它实际上是定义一个做一些事情的方法:

  1. 第一次在规范(it 块)内调用该方法时,它将执行该块,缓存结果并返回它
  2. 同一规范内的任何其他调用(它块)只会返回缓存的结果
  3. 规范完成后,它会删除缓存的结果,以便在下一次调用时重新评估,在下一个规范

这允许仅在需要时延迟创建对象,并允许将任何状态更改限制为当前规范。

因此,底线是:RSpec 使用该符号作为方法名称的标识符,该方法名称将被生成以抽象出某些事物,让您的生活更轻松。RSpec 只不过是一种使用元编程来构建测试套件的 BDD 领域特定语言。

使用以下测试用例可以实现相同的行为:

class AuthenticatorTest < Test::Unit::TestCase
  def user
    return @user if @user
    @user = Factory.create(:user)
  end

  def subject
    return @subject if @subject
    @subject = Authenticator.new
  end

  def teardown
    @subject = nil
    @user = nil
  end

  def test_authenticate
    auth_user = subject.authenticate(user.login, user.password)
    assert_equal auth_user, user
  end
end

请注意,您可能不应该编写这样的测试用例,但它(大致)说明了 RSpec 的作用。

我希望这有帮助。

于 2013-02-13T03:34:20.893 回答
0

一些小的更正/澄清:

  1. 这是一个“rspec”测试,而不是 rake 测试。Rake 是一种自动化工具(类似于“make”),但 rspec 是一种测试工具。
  2. FactoryGirl 可以创建许多不同种类的对象。如果你定义了多个工厂,传递给#create 的符号会告诉它使用哪个工厂。它没有告诉它在你的程序中设置什么变量。
  3. let() 调用定义了一个由提供的符号名称给出的辅助方法,例如:user。该方法返回由传递给它的块确定的值,例如 FactoryGirl 将创建的用户对象。

我已经忽略了 let() 的一些细节。真正的文档在这里:

https://www.relishapp.com/rspec/rspec-core/v/2-6/docs/helper-methods/let-and-let

以我个人的经验,这种特殊模式是 nc,但单元测试往往与常规代码不同,因为它们的想法是练习代码,而不是解决实际问题。要做到这一点,您往往希望一次改变一件事,这样事情就会变得重复——但是 ruby​​ 提供了许多工具来减少打字并保持可读性。

编辑:有关使用此模式的示例,请参阅注释。它就在我的鼻子底下...... :)

于 2013-02-13T01:59:50.270 回答