模块:RSpec::Core::Hooks

包含在
Configuration, ExampleGroup
定义在
lib/rspec/core/hooks.rb

概述

提供 beforeafteraround 钩子,作为支持常见设置和拆卸的一种手段。此模块扩展到 ExampleGroup,使方法在任何 describecontext 块中可用,并在 Configuration 中包含,使它们在配置对象上可用,以定义全局设置或拆卸逻辑。

实例方法摘要 折叠

实例方法细节

#after(&block) ⇒ void #after(scope, &block) ⇒ void #after(scope, *conditions, &block) ⇒ void #after(conditions, &block) ⇒ void 也称为: prepend_after

注意

:example:context 作用域也分别可用作 :each:all。请使用您喜欢的任何一个。

注意

:suite 作用域仅支持在 RSpec.configuration 上注册的钩子,因为它们独立于任何示例或示例组。

声明一个代码块,在每个示例后运行(使用 :example)或在上下文中所有示例完成后运行一次(使用 :context)。有关排序的更多信息,请参阅 #before

异常

即使 before 钩子或示例中存在异常,也保证 after 钩子会运行。当在 after 块中引发异常时,该异常会被捕获以供以后报告,并且会运行后续的 after 块。

顺序

after 钩子存储在三个作用域中,它们按顺序运行::example:context:suite。它们还可以声明在几个不同的位置:RSpec.configure、父组、当前组。它们按以下顺序运行

after(:example) # Declared in the current group.
after(:example) # Declared in a parent group.
after(:example) # Declared in RSpec.configure.
after(:context) # Declared in the current group.
after(:context) # Declared in a parent group.
after(:context) # Declared in RSpec.configure.
after(:suite)   # Declared in RSpec.configure.

这与 before 钩子运行的顺序相反。同样,如果在任何示例组中声明了多个 after,它们会按声明的顺序相反运行。同样,around 钩子将在任何 after 示例钩子调用之后运行,但在任何 after 上下文钩子之前运行。

重载

  • #after(scope, &block) ⇒void

    参数

    • scope (Symbol)

      :example:context:suite(默认为 :example

  • #after(scope, *conditions, &block) ⇒void

    参数

    • scope (Symbol)

      :example:context:suite(默认为 :example

    • conditions (Array<Symbol>, Hash)

      将此钩子限制为与这些条件匹配的示例,例如 after(:example, :ui => true) { ... } 仅在使用 :ui => true 声明的示例或组中运行。符号将转换为带有 true 值的哈希条目。

  • #after(conditions, &block) ⇒void

    参数

    • conditions (Hash)

      将此钩子限制为与这些条件匹配的示例,例如 after(:example, :ui => true) { ... } 仅在使用 :ui => true 声明的示例或组中运行。

另请参阅

277
278
279
# File 'lib/rspec/core/hooks.rb', line 277
def after(*args, &block)
  hooks.register :prepend, :after, *args, &block
end

#append_after(*args, &block) ⇒void

block 添加到同一作用域(:example:context:suite)中 after 块列表的末尾。

有关作用域语义,请参阅 #after

287
288
289
# File 'lib/rspec/core/hooks.rb', line 287
def append_after(*args, &block)
  hooks.register :append, :after, *args, &block
end

#around(&block) ⇒ void #around(scope, &block) ⇒ void #around(scope, *conditions, &block) ⇒ void #around(conditions, &block) ⇒ void

注意

around 的语法类似于 beforeafter,但语义却大不相同。beforeafter 钩子是在与它们关联的示例的上下文中运行的,而 around 钩子实际上负责运行示例。因此,around 钩子无法直接访问在示例及其关联的 beforeafter 钩子内提供的资源。

注意

:example/:each 是唯一支持的作用域。

声明一个代码块,其中一部分将在示例之前运行,而另一部分将在示例之后运行。您有责任运行该示例

around(:example) do |ex|
  # Do some stuff before.
  ex.run
  # Do some stuff after.
end

产生的示例别名为 runcall,这使您可以像处理 Proc 一样处理它。这在处理使用块或 proc 语法管理自身设置和拆卸的库时特别有用,例如

around(:example) {|ex| Database.transaction(&ex)}
around(:example) {|ex| FakeFS(&ex)}

顺序

around 钩子围绕示例及其钩子执行。

这意味着在任何 before 上下文钩子之后,但在任何 before 示例钩子之前,以及在任何 after 示例钩子之后,但在任何 after 上下文钩子之前。

它们不是 before/after 的同义词。

重载

  • #around(scope, &block) ⇒void

    参数

    • scope (Symbol)

      :example(默认为 :example)用于与 beforeafter 的语法一致,但 :example/:each 是唯一支持的值。

  • #around(scope, *conditions, &block) ⇒void

    参数

    • scope (Symbol)

      :example(默认为 :example)用于与 beforeafter 的语法一致,但 :example/:each 是唯一支持的值。

    • conditions (Array<Symbol>, Hash)

      将此钩子限制为与这些条件匹配的示例,例如 around(:example, :ui => true) { ... } 仅在使用 :ui => true 声明的示例或组中运行。符号将转换为带有 true 值的哈希条目。

  • #around(conditions, &block) ⇒void

    参数

    • conditions (Hash)

      将此钩子限制为与这些条件匹配的示例,例如 around(:example, :ui => true) { ... } 仅在使用 :ui => true 声明的示例或组中运行。

产出

349
350
351
# File 'lib/rspec/core/hooks.rb', line 349
def around(*args, &block)
  hooks.register :prepend, :around, *args, &block
end

#before(&block) ⇒ void #before(scope, &block) ⇒ void #before(scope, *conditions, &block) ⇒ void #before(conditions, &block) ⇒ void 也称为: append_before

注意

:example:context 作用域也分别可用作 :each:all。请使用您喜欢的任何一个。

注意

:suite 作用域仅支持在 RSpec.configuration 上注册的钩子,因为它们独立于任何示例或示例组。

声明一个代码块,在每个示例之前运行(使用 :example)或在任何示例之前运行一次(使用 :context)。这些通常直接声明在它们适用的 ExampleGroup 中,但它们也可以在多个组之间共享。

您还可以使用 before(:suite) 在运行任何示例组之前运行一个代码块。这应该声明在 RSpec.configure 中。

before(:example)before(:context) 中声明的实例变量在每个示例中都可访问。

顺序

before 钩子存储在三个作用域中,它们按顺序运行::suite:context:example。它们还可以声明在几个不同的位置:RSpec.configure、父组、当前组。它们按以下顺序运行

before(:suite)    # Declared in RSpec.configure.
before(:context)  # Declared in RSpec.configure.
before(:context)  # Declared in a parent group.
before(:context)  # Declared in the current group.
before(:example)  # Declared in RSpec.configure.
before(:example)  # Declared in a parent group.
before(:example)  # Declared in the current group.

如果在任何一个示例组中声明了多个 before,它们会按声明的顺序运行。无论 around 钩子在何处声明,它们都将在 before 上下文钩子之后执行,但在任何 before 示例钩子之前执行。

条件

当您向 before(:example)before(:context) 添加条件哈希时,RSpec 仅将该钩子应用于与条件匹配的组或示例。例如

RSpec.configure do |config|
  config.before(:example, :authorized => true) do
     :authorized_user
  end
end
RSpec.describe Something, :authorized => true do
  # The before hook will run in before each example in this group.
end
RSpec.describe SomethingElse do
  it "does something", :authorized => true do
    # The before hook will run before this example.
  end
  it "does something else" do
    # The hook will not run before this example.
  end
end

请注意,过滤后的配置 :context 钩子仍然可以应用于具有匹配元数据的单个示例。就像 Ruby 的对象模型是每个对象都有一个仅有一个实例的单例类一样,RSpec 的模型是每个示例都有一个包含单个示例的单例示例组。

警告:before(:suite, :with => :conditions)

条件哈希用于与特定示例匹配。由于 before(:suite) 不是针对任何特定示例或组运行的,因此与 :suite 一起传递的条件实际上会被忽略。

异常

当在 before 块中引发异常时,RSpec 会跳过任何后续的 before 块和示例,但会运行所有 after(:example)after(:context) 钩子。

警告:隐式 before

before 钩子也可以在共享上下文中声明,这些共享上下文要么被您隐式包含,要么被扩展库隐式包含。由于 RSpec 按每个作用域内的声明顺序运行这些钩子,因此加载顺序很重要,并且当一个 before 块依赖于在另一个 before 块中准备好的状态时,会导致混乱的结果,而该 before 块将在稍后运行。

警告:before(:context)

使用 before(:context) 来提高速度非常诱人,但我们建议您避免这样做,因为有很多问题,以及一些根本无法正常运行的东西。

上下文

before(:context) 在生成的用于提供块组上下文的示例中运行。

实例变量

before(:context) 中声明的实例变量在组中的所有示例之间共享。这意味着每个示例都可以更改共享对象的​​状态,从而导致排序依赖关系,这使得难以推理故障。

不支持的 RSpec 结构

RSpec 有几种结构会自动在每个示例之间重置状态。这些结构不打算在 before(:context) 中使用。

  • let 声明
  • subject 声明
  • 任何模拟、存根或测试双重声明

其他框架

模拟对象框架和数据库事务管理器(如 ActiveRecord)通常围绕在示例之前进行设置,运行该示例,然后拆卸的想法而设计。这意味着模拟和存根(有时)可以在 before(:context) 中声明,但在第一个实际示例运行之前被拆卸。

您 *可以* 在 rspec-rails 的 before(:context) 中创建数据库支持的模型对象,但它不会为您包装在事务中,因此您需要自己负责在 after(:context) 块中清理。

示例

ExampleGroup 中声明的 before(:example)


RSpec.describe Thing do
  before(:example) do
    @thing = Thing.new
  end
  it "does something" do
    # Here you can access @thing.
  end
end

ExampleGroup 中声明的 before(:context)


RSpec.describe Parser do
  before(:context) do
    File.open(file_to_parse, 'w') do |f|
      f.write <<-CONTENT
        stuff in the file
      CONTENT
    end
  end
  it "parses the file" do
    Parser.parse(file_to_parse)
  end
  after(:context) do
    File.delete(file_to_parse)
  end
end

重载

  • #before(scope, &block) ⇒void

    参数

    • scope (Symbol)

      :example:context:suite(默认为 :example

  • #before(scope, *conditions, &block) ⇒void

    参数

    • scope (Symbol)

      :example:context:suite(默认为 :example

    • conditions (Array<Symbol>, Hash)

      将此钩子限制为匹配这些条件的示例,例如 before(:example, :ui => true) { ... } 仅与使用 :ui => true 声明的示例或组一起运行。符号将转换为具有 true 值的哈希条目。

  • #before(conditions, &block) ⇒void

    参数

    • conditions (Hash)

      将此钩子限制为匹配这些条件的示例,例如 before(:example, :ui => true) { ... } 仅与使用 :ui => true 声明的示例或组一起运行。

另请参阅

200
201
202
# File 'lib/rspec/core/hooks.rb', line 200
def before(*args, &block)
  hooks.register :append, :before, *args, &block
end

#prepend_before(*args, &block) ⇒void

block 添加到同一作用域(:example:context:suite)中 before 块列表的前面。

有关作用域语义,请参见 #before

210
211
212
# File 'lib/rspec/core/hooks.rb', line 210
def prepend_before(*args, &block)
  hooks.register :prepend, :before, *args, &block
end