10

我想根据外部数据源动态生成一系列规范。具体来说,我有一个已下载的 Google 电子表格,电子表格中的每一行都应用于自己的测试用例:

describe "Cases" do
  before(:all) do
    # Download spreadsheet and
    # populate cases in DB
  end

  Cases.each do |case|
    it "Case #{case.num}" do
      # spec
    end
  end
end

这不起作用,因为对于使用 RSpec 的初学者来说,(据我了解)在编译时不会“看到”任何规范,因此before(:all)永远不会执行。如果我要放置一个空it块,它会before(:all)执行,但是我会遇到Cases.each在其他任何事情之前评估的问题,这是空的,因为它还没有被before(:all)块填充。

简而言之,我很困惑,我对 RSpec 的理解似乎很有限。我想获取数据,使用该数据设置一堆规范,然后让它们由 RSpec 运行。如果 Cases 是预先设置的数组(在 describe 块之外),这将(可能?)工作,但我需要在运行时设置它。我想在 RSpec 中做些什么?

4

2 回答 2

3

删除before块。

a 中的代码在describe加载 rspec 时执行。它的缺点是即使不运行这些规范,它也会下载并扫描工作表。

您将不得不处理的下一个问题是数据库在每个规范之后都会被清空。因此,您需要为下面创建的每个规范在 before 块中重新填充数据库。

我会强调我不会推荐这个,我会深入考虑动态拉取测试用例。考虑证明您的代码在没有外部数据的情况下可以正常工作的通用测试。或者提取电子表格的副本,将其保存spec/assets并加载,并针对该特定示例进行更直接的测试。

describe "Cases" do
  # Download spreadsheet and
  # populate cases in DB
  before(:each) do
    # repopulate the DB
  end

  Cases.each do |case|
    it "Case #{case.num}" do
      # spec
    end
  end

  # clean out the db so that the first executed test is not polluted
end
于 2013-01-25T18:03:40.907 回答
3

这不起作用,因为... RSpec 没有...在编译时“看到”任何规范...

我想知道为什么,并做了一些研究。如果您检查以下代码和输出,您可以得出一些结论:

  • Ruby 中没有编译时间
  • 类/模块的主体被立即评估和执行
  • describe方法也是如此
  • 块的主体describe由 RSpec 立即处理
  • RSpec 存储before块和it块以供以后执行
  • 当文件解析终止时,RSpec 开始报告 describe字符串并执行存储的before/it示例块

下面的代码演示了这一点。

module SO
    puts '>>>>> Ruby sees module SO and executes/evaluates its body'
    cases = [1,2,3]

    describe "SO Cases" do
        puts "in module SO, RSpec sees describe Cases self=#{self}"
        before(:each) do
            puts '  in before(:all)'
        end

        cases.each do |case_|
            puts "    in each loop with case_=#{case_}"
            it "Case #{case_}" do
                puts "        check spec for case #{case_}"
            end
        end
    end
end

module M # to avoid "warning: class variable access from toplevel"
    puts '>>>>> Ruby sees module M and executes/evaluates its body'
    describe "M Cases" do
        puts "in module M, RSpec sees describe Cases self=#{self}, ancestors :"
        ancestors.each {|a| puts "    #{a}"}
        print 'self.methods.grep(/^it/) : '; p self.methods.grep(/^it/).sort
        before(:all) do
            puts "  in before(:all) self=#{self}"
            @@cases = [1,2,3]
            puts "  ... now cases=#{@@cases}"
            File.open('generated_cases.rb', 'w') do |fgen|
                fgen.puts 'puts "\n*** inside generated_cases.rb ***"'
                fgen.puts 'module GenSpecs'
                fgen.puts "puts '>>>>> Ruby sees module GenSpecs and executes/evaluates its body'"
                fgen.puts '    describe "GenSpecs Cases" do'
                fgen.puts '        puts "in module GenSpecs, RSpec sees describe Cases"'
                @@cases.each do |case_|
                    puts "    in each loop with case_=#{case_}"
                    fgen.puts <<-IT_SPECS
        it "Case #{case_}" do
            puts "    some spec for case_=#{case_}"
        end
        IT_SPECS
                end
                fgen.puts '    end'
                fgen.puts 'end # module GenSpecs'
                fgen.puts "puts '>>>>> end of ruby file generated_cases.rb <<<<<'"
            end
            puts 'file generated_cases.rb has been closed'
            require_relative 'generated_cases'
        end

        it 'has loaded Cases' do
            @@cases.should_not be_empty
        end
    end
end
puts '>>>>> end of ruby file t2_spec.rb <<<<<'

执行 :

$ ruby -v
ruby 1.9.2p320 (2012-04-20 revision 35421) [x86_64-darwin12.2.0]
$ rspec --version
2.12.2
$ rspec --format nested t2_spec.rb 
>>>>> Ruby sees module SO and executes/evaluates its body
in module SO, RSpec sees describe Cases self=#<Class:0x007fcaf49a6e80>
    in each loop with case_=1
    in each loop with case_=2
    in each loop with case_=3
>>>>> Ruby sees module M and executes/evaluates its body
in module M, RSpec sees describe Cases self=#<Class:0x007fcaf2852e28>, ancestors :
    #<Class:0x007fcaf2852e28>
    RSpec::Core::ExampleGroup
    ...
self.methods.grep(/^it/) : [:it, :it_behaves_like, :it_should_behave_like, :its]
>>>>> end of ruby file t2_spec.rb <<<<<

SO Cases
  in before(:all)
        check spec for case 1
  Case 1
  in before(:all)
        check spec for case 2
  Case 2
  in before(:all)
        check spec for case 3
  Case 3

M Cases
  in before(:all) self=#<RSpec::Core::ExampleGroup::Nested_2:0x007fcaf2836ca0>
  ... now cases=[1, 2, 3]
    in each loop with case_=1
    in each loop with case_=2
    in each loop with case_=3
file generated_cases.rb has been closed

*** inside generated_cases.rb ***
>>>>> Ruby sees module GenSpecs and executes/evaluates its body
in module GenSpecs, RSpec sees describe Cases
>>>>> end of ruby file generated_cases.rb <<<<<
  has loaded Cases

GenSpecs Cases
    some spec for case_=1
  Case 1
    some spec for case_=2
  Case 2
    some spec for case_=3
  Case 3

Finished in 0.01699 seconds
7 examples, 0 failures

创建一个文件并以这种方式要求它是为了演示,可能不适用于您的情况。我建议分两个阶段进行:首先阅读电子表格并创建一个包含 adescribe和几个it示例的 file.rb。在第二阶段,您启动 Ruby 来处理生成的文件。

于 2013-01-26T10:28:12.810 回答