1

使用 Rails 5 和 Rspec 3.7 我有一个相当简单的测试,目前正在运行(有时通过有时失败)。通过到目前为止我所做的调试,我保存到我的测试数据库的值似乎没有在测试之间持续存在,但我不知道为什么会这样。

这是带有关于扑动测试的评论的测试套件(其余的始终通过)

describe ResourceCenterController, type: :controller do
  before(:each) do
    @platform_instance = FactoryBot.create(:platform_instance)
    @domain = FactoryBot.create(:domain, platform_instance: @platform_instance)
    @user = FactoryBot.create(:user, platform_instance: @platform_instance, first_name: "O'flaggan")
  end

  context 'when user IS signed in' do
    before(:each) do
      login_user(@user)
    end

    context 'when user in ONE community' do
      before(:each) do
        @user.communities = [@platform_instance.communities.first]
        @user.save!
      end

      describe '#index' do
        before(:each) do
          @rc = FactoryBot.create(:resource_center, platform_instance: @platform_instance, launch_at: nil, expire_at: nil)
        end

        context 'when community assigned NO resource centers' do
          before(:each) do
            @rc.communities = []
            @rc.save!

            get :index
          end

          it_behaves_like '200 w name in body' do
            let(:names) { ['There are no files for your review at the moment.'] }
          end
        end

        context 'when community assigned ONE resource center' do
          before(:each) do
            @rc.communities = [@user.communities.first]
            @rc.save!
          end

          context 'when resource center assigned NO mediafiles' do
            before(:each) do
              @rc.mediafiles = []
              @rc.save!

              get :index
            end

            it_behaves_like '200 w name in body' do
              let(:names) { ['There are no files for your review at the moment.'] }
            end
          end

          # this test is flapping
          # sometimes it will persist the mediafile and it will show up
          # other times it will be saved, why is that?
          context 'when resource center assigned ONE mediafile' do
            before(:each) do
              @mediafile = FactoryBot.create(:mediafile, platform_instance: @platform_instance)
              @rc.mediafiles << @mediafile
              @rc.save!

              get :index
            end

            it_behaves_like '200 w name in body' do
              let(:names) { ["#{@mediafile.name}"] }
            end
          end
        end
      end
    end
  end
end

这是共享的上下文

shared_context '200 w name in body' do
    it 'returns 200' do
      expect(response.status).to eq(200)
    end

    it 'renders the view' do
      names.each do |name|
        expect(response.body).to include(name)
      end
    end
end

编辑:我了解了 bisect 标志并使用此输出运行它

Bisect started using options: "spec/controllers/resource_center_controller_spec.rb"
Running suite to find failures... (7.39 seconds)
Starting bisect with 1 failing example and 5 non-failing examples.
Checking that failure(s) are order-dependent... failure appears to be order-dependent

Round 1: bisecting over non-failing examples 1-5 .. multiple culprits detected - splitting candidates (13.84 seconds)
Round 2: bisecting over non-failing examples 1-3 . ignoring examples 1-2 (6.95 seconds)
Round 3: bisecting over non-failing examples 4-5 . ignoring example 4 (6.75 seconds)
Bisect complete! Reduced necessary non-failing examples from 5 to 2 in 34.1 seconds.

The minimal reproduction command is:
  rspec ./spec/controllers/resource_center_controller_spec.rb[1:1:1:1:2:1:1:1,1:1:1:1:2:2:1:1,1:1:1:1:2:2:1:2]

编辑:这是媒体文件的工厂

FactoryBot.define do
  # pi = PlatformInstance.select
  factory :mediafile do
    name { Faker::Simpsons.character }
    platform_instance_uuid { PlatformInstance.first.uuid } # stick to platforminstance.first for now
    platform_instance { PlatformInstance.first }           # had tried to use a variable, but was
                                                           # not working
    description { Faker::Simpsons.quote }
    document { File.new("#{Rails.root}/spec/support/fixtures/mediafiles/document_01.pdf") }
    image { File.new("#{Rails.root}/spec/support/fixtures/mediafiles/image_01.jpg") }

    # review_with_mediafiles will create mediafile data after the review has been created
    factory :mediafile_with_review do
      after(:create) do |mediafile, evaluator|
        create(:review, mediafile: mediafile)
      end
    end
  end
end

这里是资源中心的工厂

FactoryBot.define do
  factory :resource_center do
    title { Faker::Company.catch_phrase }
    description { Faker::Lorem.paragraph(10) }
    launch_at { Time.now }
    expire_at { Time.now + 1.week }
    platform_instance_uuid { PlatformInstance.first.uuid } # stick to PlatformInstance.first for now
    platform_instance { PlatformInstance.first }           # had tried to use a variable, but was
                                                           # not working
    status { [:testing, :live].sample }

    # review_with_mediafiles will create mediafile data after the review has been created
    # this factory inherits everything from the factory it is nested under
    factory :resource_center_with_mediafiles do
      after(:create) do |resource_center, evaluator|
        create(:mediafile, resource_centers: [resource_center])
      end
    end

  end
end

控制器方法本身相当简单

def index
  @resource_centers = current_user.resource_centers.within_dates
end

current_user 变量是在应用程序控制器中分配的,我认为这里没有必要包含它。该视图也相当简单,可以在下面看到

-content_for :breadcrumbs do
  =render 'layouts/shared/breadcrumbs', breadcrumbs: [link_to('Home', user_root_path), 'Resource Center']

  -files_present = false
  -@resource_centers.each do |resource_center|
    -if resource_center.mediafiles.present?
      -files_present = true

      %h3.color-primary= resource_center.title.html_safe
      =resource_center.description.html_safe

      .space-above-2
        -resource_center.mediafiles.sort.each do |mediafile|
          =render 'resource_center/mediafile_item', resource_center: resource_center, mediafile: mediafile

  -if !files_present
    %h4 There are no files for your review at the moment. 

这是上面视图中呈现的部分内容。

.index-list
  .index-item.large-avatar
    .item-avatar
      =link_to resource_center_mediafile_view_path(resource_center, mediafile) do
        = image_tag mediafile.image.url

    .item-content
      .item-header= mediafile.name

      .item-attribute-list
        %span.item-attribute
          -if mediafile.duration.present?
            %strong DURATION:
            =pluralize(mediafile.duration, "minute")
          -if mediafile.document.size.to_i > 0
            %strong SIZE:
            =number_to_human_size(mediafile.document.size)

    .item-actions
      -if resource_center.downloadable
        =link_to 'Download', mediafile.download_url, class: 'mui-button default', target: '_blank'
      =link_to 'View', resource_center_mediafile_view_path(resource_center, mediafile), class: 'mui-button'

这是 spec_helper 文件:

ENV['RAILS_ENV'] ||= 'test'

require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'

RSpec.configure do |config|
  config.before(:each) do
    #only modify the request when testing controllers
    if described_class <= ApplicationController
      request.host = 'localhost:3000'
    end
  end

  config.include Rails.application.routes.url_helpers

  config.expect_with :rspec do |expectations|
    expectations.include_chain_clauses_in_custom_matcher_descriptions = true
  end

  config.mock_with :rspec do |mocks|
    mocks.verify_partial_doubles = true
  end

  config.before(:suite) do
    DatabaseCleaner.strategy = :transaction
    DatabaseCleaner.clean_with(:truncation)
  end

  config.around(:each) do |example|
    DatabaseCleaner.cleaning do
      example.run
    end
  end

  config.before(:all) do
    DatabaseCleaner.start
  end

  config.after(:all) do
    DatabaseCleaner.clean
  end

  config.shared_context_metadata_behavior = :apply_to_host_groups
  # config.include Rails.application.routes.url_helpers
end

如果还有其他有用的信息,请告诉我。我认为这是我的测试套件有问题,特别是 before(:each) 块,但我的实验并没有给我任何见解。

4

1 回答 1

4

免责声明:我没有阅读您发布的整个代码,所以我不会给您答案是什么导致了这种片状(或您称之为 flippines),但我会给您一种自己找到它的方法。(鱼与钓鱼竿的事情有点像)

使用 bisect 很棒,因为它说问题取决于顺序,所以很容易继续。

您现在可以在失败中设置断点it并调查结果与预期不同的原因。很可能junk在数据库中还有一些其他规范遗留下来的东西。

当您查明规范失败的原因时,您可以运行命令:

rspec  --format doc \ 
./spec/controllers/resource_center_controller_spec.rb[1:1:1:1:2:1:1:1,1:1:1:1:2:2:1:1,1:1:1:1:2:2:1:2]

这将告诉您测试的运行顺序(因为[1:1:1:1:2:1:1:1,1:1:1:1:2:2:1:1,1:1:1:1:2:2:1:2]不是很人性化),您可以查找使“状态不干净”的规范(提到 DB 垃圾,但可能是其他东西)

当您确定违规者时,您可以添加一些粗略的修复(比如Model.destroy_all在它之后,以确认它是原因)。

请注意,这还不是正确的修复方法。

在您确认这是真的之后 - 您已准备好寻找解决方案。这可以为您的规范使用 DBCleaner,或者修复一些行为不端或完全不同的缓存代码(当您有答案时,请随时提出另一个问题)

一个额外的注意事项:在许多项目中,规格的顺序将是随机的。在这种情况下bisecting将失败,除非您知道--seed规范失败的原因。

于 2018-06-21T09:32:17.327 回答