首页 文章

RSpec:let和a block之间有什么区别?

提问于
浏览
84

RSpec中 letbefore 块之间有什么区别?

什么时候使用?

下面的例子中有什么好方法(让或之前)?

let(:user) { User.make !}
let(:account) {user.account.make!}

before(:each) do
 @user = User.make!
 @account = @user.account.make!
end

我研究了这个stackoverflow post

但是为上面的关联内容定义let是否合适?

4 回答

  • 130

    人们似乎已经解释了他们不同的一些基本方式,但是没有解释为什么应该使用它们 .

    我认为实例变量在绝大多数规范中都没有使用,部分原因在于this answer中提到的原因,所以我不会在这里提到它们作为选项 .

    let blocks

    let 块中的代码仅在引用时执行,延迟加载这意味着这些块的排序无关紧要 . 这为您提供了大量的功能,可以通过您的规格减少重复设置 .

    一个(非常小而且做作)的例子是:

    let(:person)     { build(:person) }
    subject(:result) { Library.calculate_awesome(person, has_moustache) }
    
    context 'with a moustache' do
      let(:has_moustache) { true } 
      its(:awesome?)      { should be_true }
    end
    
    context 'without a moustache' do
      let(:has_moustache) { false } 
      its(:awesome?)      { should be_false }
    end
    

    您可以看到 has_moustache 在每种情况下的定义都不同,但不需要重复 subject 定义 . 需要注意的一点是,将使用当前上下文中定义的最后一个 let 块 . 这适用于设置大多数规范使用的默认值,如果需要可以覆盖 .

    例如,检查 calculate_awesome 的返回值,如果传递 person 模型且 top_hat 设置为true,但没有胡须:

    context 'without a moustache but with a top hat' do
      let(:has_moustache) { false } 
      let(:person)        { build(:person, top_hat: true) }
      its(:awesome?)      { should be_true }
    end
    

    关于let块的另一个注意事项是,如果您正在搜索已保存到数据库中的内容(即 Library.find_awesome_people(search_criteria) ),则不应使用它们,因为它们不会保存到数据库中,除非它们已被引用 . let!before 块是这里应该使用的 .

    此外,永远 ever 使用 before 来触发 let 块的执行,这就是 let! 的用途!

    let! blocks

    let! 块按照它们的定义顺序执行(很像前一个块) . 与块之前的一个核心差异是,您获得了对此变量的显式引用,而不是需要回退到实例变量 .

    let 块一样,如果使用相同的名称定义了多个 let! 块,则最新的块将在执行时使用 . 如果像这样使用的话,核心区别是 let! 块将被执行多次,而 let 块将仅执行最后一次 .

    before(:each) blocks

    before(:each) 是块之前的默认值,因此可以引用为 before {} ,而不是每次都指定完整 before(:each) {} .

    在一些核心情况下,我个人倾向于使用 before 块 . 如果出现以下情况,我将使用块

    • 我正在使用 Mock ,抄写或双打

    • 有任何合理大小的设置(通常这是您的工厂特性未正确设置的标志)

    • 有's a number of variables which I don'需要直接引用,但是需要进行设置

    • 我在rails中编写功能控制器测试,我想执行一个特定的测试请求(即 before { get :index } ) . 尽管在很多情况下你可以使用 subject ,但如果你不需要引用,它有时会更明确 .

    如果您发现自己为规范编写了大块 before 块,请检查您的工厂并确保您完全了解特征及其灵活性 .

    before(:all) blocks

    这些只在当前上下文(及其子代)中的规范之前执行过一次 . 如果正确编写,这些可以非常有用,因为在某些情况下这可以减少执行和工作量 .

    一个例子(根本不会影响执行时间)是为测试模拟一个ENV变量,你应该只需要做一次 .

    希望有帮助:)

  • 3

    几乎总是,我更喜欢 let . 您链接的帖子指定 let 也更快 . 但是,有时,当必须执行许多命令时,我可以使用 before(:each) ,因为当涉及许多命令时,它的语法更清晰 .

    在你的例子中,我宁愿使用 let 而不是 before(:each) . 一般来说,当只进行一些变量初始化时,我倾向于使用 let .

  • 15

    没有提到的一个很大的区别是,在第一次调用之前,用 let 定义的变量不会被实例化 . 因此,虽然 before(:each) 块会实例化所有变量, let 让's you define a number of variables you might use across multiple tests, it doesn' t自动实例化它们 . 如果您希望预先加载所有数据,那么在不知道这一点的情况下,您的测试可能会回过头来咬自己 . 在某些情况下,您甚至可能希望定义一些 let 变量,然后使用 before(:each) 块来调用每个 let 实例,以确保数据可用 .

  • 25

    看起来你正在使用机械师 . 请注意,您可能会看到make的一些问题! let(非爆炸版本)内部发生在全局fixture事务之外(如果您也使用事务性fixture),因此破坏了其他测试的数据 .

相关问题