在共享示例调用中的let中使用循环变量

时间:2016-08-10 09:16:21

标签: ruby-on-rails ruby rspec

我正在编写使用RoR& amp;虾。该PDF有一堆过滤选项(其中24个)。为了不错过任何重要的规范,我在before :context块中生成过滤器选项并将其保存在实例变量中。

我的问题出现的地方是尝试迭代所有过滤器选项并运行共享示例,以测试不同过滤器不会发生太大变化的基础知识。

这就是我的代码对所有这些过滤器和文档过滤器的看法(filter_setting方法只是访问特定@major_filter_options的帮助器):

describe 'pdf' do
  before :context do
    @major_filter_options = {}
    @major_filter_options.define_them
  end

  describe 'basic content' do
    before :context do
      @filter_options_with_docu = {}
      # find all the filters that have the docu option enabled
      @major_filter_options.each do |key, mfo|
        @filter_options_with_docu[key] = mfo if key.to_s.include? 'docu'
      end
    end

    24.times do |t| # can't access major_filter_options.size here.. it's nil.
      include_examples 'first_3_pages' do
        let(:pdf) do
          filter_options = filter_setting(@major_filter_options.keys[t])
          ProjectReport::ReportGenerator.new.generate(project, filter_options, user).render
        end
        let(:page_analysis) { PDF::Inspector::Page.analyze(pdf) }
      end
    end

    12.times do |t| # @print_options_with_docu is also nil at this point
      include_examples 'documentation_content' do
        let(:pdf) do
          filter_options = filter_setting(@filter_options_with_docu.keys[t])
          ProjectReport::ReportGenerator.new.generate(project, filter_options, user).render
        end
        let(:page_analysis) { PDF::Inspector::Page.analyze(pdf) }
      end
    end
    # ...
  end

我有两大问题:

其中一个是24.times12.times等等(其中有一堆)正在困扰我,因为它会使维护变得更加艰难。一个新的过滤器选项会改变所有的值,找到所有值来改变它们在我看来很容易出错。

另一个问题是变量迭代在这里这样的事实: 当我在12.times do |t|的任何一个内部时let实际上似乎没有迭代:

let(:pdf) do
  filter_options = filter_setting(@major_filter_options.keys[t])
  puts t
  # ...
end

puts t每次打印11次(过滤器每次都相同)。 经过一番阅读后,我找到了a gist example。这个问题看起来很相似,但遗憾的是在它周围放了describe块并没有多大作用。

24.times do |t|
  describe
    # same as before
  end
end

有趣的是,当在该设置中再次执行puts t时,每次都会为6,这让我更加困惑。

我还应该提一下,将它们拆分成这样的原因是我分享了仅适用于某些过滤器的示例。如果有人对如何更好地了解,例如迭代@major_filter_options,然后根据当前哈希值key调用某些共享示例,那我就是所有的耳朵!

1 个答案:

答案 0 :(得分:3)

关于您无法在.times块中定义的实例变量上调用before :context

RSpec分两个阶段进行。在第一阶段,它执行spec文件中的Ruby代码; letbeforeit方法存储其块以便稍后运行。在第二阶段,它实际运行测试,即letbeforeit块的内容。 before :context块在第二阶段之前不定义实例变量,因此在第一阶段运行的.times语句无法看到实例变量。

解决方案是将过滤器选项置于RSpec到达.times语句之前初始化的位置,如常量。

关于循环中的include_examples,总是使用相同的循环变量值:

include_examples包含当前上下文中的给定共享示例。如果您不止一次包含相同的示例,则示例本身将被多次包含,但最后一个包含中的let将覆盖所有先前包含中的letsThe RSpec documentation has a clear example.

解决方案是使用it_behaves_like代替include_examplesit_behaves_like将包含的示例放在嵌套示例组中,因此let不能互相覆盖。

应用这两个解决方案的方法如下:

describe 'pdf' do
  describe 'basic content' do
    MAJOR_FILTER_OPTIONS = # code that initializes them
    MAJOR_FILTER_OPTIONS.values.each do |filter_option|
      it_behaves_like 'first_3_pages' do
        let(:pdf) do
          filter_options = filter_setting(filter_option)
          ProjectReport::ReportGenerator.new.generate(project, filter_options, user).render
        end
        let(:page_analysis) { PDF::Inspector::Page.analyze(pdf) }
      end
    end

    FILTER_OPTIONS_WITH_DOCU = # code that chooses them from MAJOR_FILTER_OPTIONS
    FILTER_OPTIONS_WITH_DOCU.values.each do |filter_option|
      it_behaves_like 'documentation_content' do
        let(:pdf) do
          filter_options = filter_setting(filter_option)
          ProjectReport::ReportGenerator.new.generate(project, filter_options, user).render
        end
        let(:page_analysis) { PDF::Inspector::Page.analyze(pdf) }
      end
    end

  end
end