ruby 2.1 rails 4太阳黑子solr测试套件失败但单独通过

时间:2014-12-18 19:23:39

标签: ruby-on-rails ruby solr rspec

我的团队已经对这个问题感到困惑了一段时间,并且不知道接下来要去哪里尝试。下面的规范在单独运行时可以正常工作,但是,当我们通过bundle exec ./bin/rspec spec在我们的套件中运行时,这两个测试每次都会失败:

  • GET / external-products /:id / deals
  • GET / external-products / search / deals

我们尝试了很多不同的方法来解决这个问题,我开始怀疑上述规范之外的其他内容。所以我必须转向堆神,并请求那里有人有更好的方法,甚至更好的问题来问这个问题。

Rspec错误:

  8) Retailigence Products and Locations GET /external-products/search/deals Search a given region for related deals by query string
     Failure/Error: expect(response_body).to have_json_type(Integer).at_path('deals/0/id')
     JsonSpec::MissingPath:
       Missing JSON path "deals/0/id"
     # ./spec/features/external_products_spec.rb:151:in `block (3 levels) in <top (required)>'
     # -e:1:in `<main>'

  9) Retailigence Products and Locations GET /external-products/:id/deals Search a given region for deals related to a particular product
     Failure/Error: expect(response_body).to have_json_type(Integer).at_path('deals/0/id')
     JsonSpec::MissingPath:
       Missing JSON path "deals/0/id"
     # ./spec/features/external_products_spec.rb:105:in `block (3 levels) in <top (required)>'
     # -e:1:in `<main>'

这是我们的spec_helper.rb:

require 'rubygems'

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

require File.expand_path('../../config/environment', __FILE__)
require 'rspec/rails'
require 'email_spec'
require 'pry'
require 'rspec_api_documentation/dsl'
require 'sunspot/rails/spec_helper'
require 'sunspot_test/rspec'

# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir[Rails.root.join('spec/concerns/**/*.rb')].each { |f| require f }
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }


# Checks for pending migrations before tests are run.
# If you are not using ActiveRecord, you can remove this line.
ActiveRecord::Migration.maintain_test_schema!

# Model specs: type: :model
# Controller specs: type: :controller
# Request specs: type: :request
# Feature specs: type: :feature
# View specs: type: :view
# Helper specs: type: :helper
# Mailer specs: type: :mailer
# Routing specs: type: :routing

RSpec.configure do |config|
  config.order = 'random'
  config.seed = srand % 0xFFFF
  config.infer_spec_type_from_file_location!
  config.use_transactional_fixtures = false
  config.infer_base_class_for_anonymous_controllers = false
  config.before(:each) { GC.disable }
  config.after(:each) { GC.enable }

  config.include FactoryGirl::Syntax::Methods
  config.include JsonSpec::Helpers
  config.include Stubs
  config.include LoginHelper
  config.include SolrSpecHelper
  config.include SunspotMatchers
  config.include Devise::TestHelpers, type: :controller

  config.before do
    Sunspot.session = SunspotMatchers::SunspotSessionSpy.new(Sunspot.session)
  end

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

  config.before(:each) do
    DatabaseCleaner.strategy = :transaction
  end

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

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

RspecApiDocumentation.configure do |config|
  config.format = :json
end

features / external_product_service_spec.rb

resource 'Retailigence Products and Locations', type: :feature, api: true, slow: true, sunspot: true do
  before(:all) do
    log_in_as_client!
  end

  let(:id) { '7c381d47-d251-457a-a2d2-930c8993a5fa' }
  let(:lat) { 39.74585 }
  let(:long) { -104.998929 }
  let(:q) { 'whiteboard cleaner' }

  get '/external-products/:id' do
    parameter :id, 'The external id to search', required: true
    parameter :lat, 'The latitude to search', required: true
    parameter :long, 'The longitude to search', required: true
    parameter :radius, 'The radius, in miles, to search'

    example 'Search a given region for products matching a given external product id' do
      do_request user_email: @user.email, user_token: @token, timestamp: @timestamp

      expect(response_body).to have_json_type(Array).at_path('external_products')
      expect(response_body).to have_json_type(String).at_path('external_products/0/id')
      expect(response_body).to have_json_type(String).at_path('external_products/0/name')
      expect(response_body).to have_json_type(String).at_path('external_products/0/seo_slug')
      expect(response_body).to have_json_type(String).at_path('external_products/0/description')
      expect(response_body).to have_json_type(Array).at_path('external_products/0/images')
      expect(response_body).to have_json_type(String).at_path('external_products/0/price')
    end
  end

  get '/external-products/:id/external-stores' do
    parameter :id, 'The external id to search', required: true
    parameter :lat, 'The latitude to search', required: true
    parameter :long, 'The longitude to search', required: true
    parameter :radius, 'The radius, in miles, to search'

    example 'Search a given region for stores which have the product matching a given external product id' do
      do_request user_email: @user.email, user_token: @token, timestamp: @timestamp

      expect(response_body).to have_json_type(Array).at_path('external_stores')
      expect(response_body).to have_json_type(String).at_path('external_stores/0/id')
      expect(response_body).to have_json_type(String).at_path('external_stores/0/name')
      expect(response_body).to have_json_type(String).at_path('external_stores/0/store_logo')
      expect(response_body).to have_json_type(Float).at_path('external_stores/0/longitude')
      expect(response_body).to have_json_type(Float).at_path('external_stores/0/latitude')
      expect(response_body).to have_json_type(Float).at_path('external_stores/0/distance')
      expect(response_body).to have_json_type(String).at_path('external_stores/0/city')
      expect(response_body).to have_json_type(String).at_path('external_stores/0/address')
      expect(response_body).to have_json_type(String).at_path('external_stores/0/zip')
      expect(response_body).to have_json_type(String).at_path('external_stores/0/state')
      expect(response_body).to have_json_type(String).at_path('external_stores/0/phone_number')
    end
  end

  get '/external-products/search' do
    parameter :q, 'The query string to search', required: true
    parameter :lat, 'The latitude to search', required: true
    parameter :long, 'The longitude to search', required: true
    parameter :radius, 'The radius, in miles, to search'

    example 'Search a given region for products by query string' do
      do_request q: q, user_email: @user.email, user_token: @token, timestamp: @timestamp

      expect(response_body).to have_json_type(Array).at_path('external_products')
      expect(response_body).to have_json_type(String).at_path('external_products/0/id')
      expect(response_body).to have_json_type(String).at_path('external_products/0/name')
      expect(response_body).to have_json_type(String).at_path('external_products/0/seo_slug')
      expect(response_body).to have_json_type(String).at_path('external_products/0/description')
      expect(response_body).to have_json_type(Array).at_path('external_products/0/images')
      expect(response_body).to have_json_type(String).at_path('external_products/0/price')
    end
  end

  get '/external-products/:id/deals' do
    before(:each) do
      solr_setup
      store.location.save
      store.location.reload
      store.location.index!
    end

    let(:retailer) { create :retailer }
    let!(:deal) { create :deal, retailer_id: retailer.id }
    let!(:store) do
      create :store, retailer_id: retailer.id,
             location: (create :location, latitude: lat, longitude: long)
    end
    let!(:retailigence_retailer) do
      create :retailigence_retailers_retailer, retailer_id: retailer.id,
             retailigence_retailer_id: '39bfd9a5-f979-4ef1-816b-f9a38093494a'
    end
    let!(:content_location) do
      create :content_location, store_id: store.id,
             locatable_id: deal.id, locatable_type: 'Deal'
    end

    parameter :id, 'The external id to search', required: true
    parameter :lat, 'The latitude to search', required: true
    parameter :long, 'The longitude to search', required: true
    parameter :radius, 'The radius, in miles, to search'

    example 'Search a given region for deals related to a particular product' do
      do_request id: id, user_email: @user.email, user_token: @token, timestamp: @timestamp

      expect(response_body).to have_json_type(Array).at_path('deals')
      expect(response_body).to have_json_type(Integer).at_path('deals/0/id')
      expect(response_body).to have_json_type(Integer).at_path('deals/0/retailer_id')
      expect(response_body).to have_json_type(String).at_path('deals/0/title')
      expect(response_body).to have_json_type(String).at_path('deals/0/seo_slug')
      expect(response_body).to have_json_type(String).at_path('deals/0/name')
      expect(response_body).to have_json_type(String).at_path('deals/0/sort_name')
      expect(response_body).to have_json_type(String).at_path('deals/0/description')
      expect(response_body).to have_json_type(:boolean).at_path('deals/0/is_local')
      expect(response_body).to have_json_type(:boolean).at_path('deals/0/is_featured')
      expect(response_body).to have_json_type(Array).at_path('images')
    end
  end

  get '/external-products/search/deals' do
    before(:each) do
      solr_setup
      store.location.save
      store.location.reload
      store.location.index!
    end

    let(:retailer) { create :retailer }
    let!(:deal) { create :deal, retailer_id: retailer.id }
    let!(:store) do
      create :store, retailer_id: retailer.id,
             location: (create :location, latitude: lat, longitude: long)
    end
    let!(:retailigence_retailer) do
      create :retailigence_retailers_retailer, retailer_id: retailer.id,
             retailigence_retailer_id: 'eea1722b-ac89-4cce-95ec-26c2414646d7'
    end
    let!(:content_location) do
      create :content_location, store_id: store.id,
             locatable_id: deal.id, locatable_type: 'Deal'
    end

    parameter :q, 'The query string to search', required: true
    parameter :lat, 'The latitude to search', required: true
    parameter :long, 'The longitude to search', required: true
    parameter :radius, 'The radius, in miles, to search'

    example 'Search a given region for related deals by query string' do
      do_request q: q, lat: lat, long: long,
                 user_email: @user.email, user_token: @token, timestamp: @timestamp

      expect(response_body).to have_json_type(Array).at_path('deals')
      expect(response_body).to have_json_type(Integer).at_path('deals/0/id')
      expect(response_body).to have_json_type(Integer).at_path('deals/0/retailer_id')
      expect(response_body).to have_json_type(String).at_path('deals/0/title')
      expect(response_body).to have_json_type(String).at_path('deals/0/seo_slug')
      expect(response_body).to have_json_type(String).at_path('deals/0/name')
      expect(response_body).to have_json_type(String).at_path('deals/0/sort_name')
      expect(response_body).to have_json_type(String).at_path('deals/0/description')
      expect(response_body).to have_json_type(:boolean).at_path('deals/0/is_local')
      expect(response_body).to have_json_type(:boolean).at_path('deals/0/is_featured')
      expect(response_body).to have_json_type(Array).at_path('images')
    end
  end
end

retailigence_service.rb:

class RetailigenceService
  NEGATIVE_KEYWORDS = 'AND !DVD AND !CD AND !"compact disc"'
  MAX_QUERY_TIME = 5000

  attr_accessor :products, :locations, :params

  def self.products(params = {})
    fetch(params).products
  end

  def self.locations(params = {})
    fetch_locations(params).locations
  end

  def self.fetch(params = {})
    service = new
    service.fetch(params)
    service
  end

  def self.fetch_locations(params = {})
    service = new
    service.fetch_locations(params)
    service
  end

  def fetch(params = {})
    @params = { offset: 0, limit: 25 }.merge(params)
    @params.symbolize_keys!

    search_result = Retailigence::Product.search(search_params)
    @products = search_result.results
    build_products
  rescue Retailigence::NoResults
    @products = []
  rescue Retailigence::APIException
    @products = []
  end

  def fetch_locations(params = {})
    @params = { offset: 0, limit: 25 }.merge(params)
    @params.symbolize_keys!

    Rails.logger.debug "[SENDING] #{location_params}"

    search_result = Retailigence::Location.search(location_params)
    @locations = search_result.results.map { |retailer| retailer.locations }.flatten
    build_locations
  rescue Retailigence::NoResults
    @locations = []
  rescue Retailigence::APIException
    @locations = []
  end

  private

  def keywords
    keywords = @params[:keywords] || ''
    keywords << ' ' << NEGATIVE_KEYWORDS
    keywords.gsub(/^\ AND\ /, '')
  end

  def page_size
    @params[:limit]
  end

  def page
    (@params[:offset] / @params[:limit]) + 1
  end

  def location_params
    {
      userlocation: @params[:userlocation],
      requestorid: RETAILIGENCE_REQUESTOR_ID,
      productid: @params[:productid],
      pagesize: page_size,
      page: page,
      maxquerytime: MAX_QUERY_TIME,
      excludeadultcontent: true
    }
  end

  def search_params
    search_params = {
      userlocation: @params[:userlocation],
      requestorid: RETAILIGENCE_REQUESTOR_ID,
      pagesize: page_size, page: page,
      maxquerytime: MAX_QUERY_TIME,
      excludeadultcontent: true
    }

    search_params[:keywords] = keywords unless @params[:product_id]
    search_params[:productid] = @params[:product_id] if @params[:product_id]
    search_params
  end

  def format_price_prefix(prefix)
    prefix = '$' if prefix == 'USD'
    prefix
  end

  def build_products
    @products.map! do |p|
      p.images ||= []
      p.description_long ||= ''
      RetailigenceProduct.new(
        id: p.id, name: p.name, seo_slug: p.name.slugify,
        description: p.description_long,
        images: p.images.map { |img| img.link },
        price: "#{format_price_prefix p.msrp_currency}#{p.price}",
        retailigence_retailer_id: p.location.retailer.id)
    end
  end

  def build_locations
    @locations.map! do |l|
      RetailigenceLocation.new(
        id: l.id, name: l.retailer.name,
        latitude: l.latitude, longitude: l.longitude,
        phone_number: l.phone, address: l.address.address1,
        city: l.address.city, state: l.address.state,
        zip: l.address.postal, store_logo: l.retailer.logo,
        distance: l.distance.distance)
    end
  end
end

这是我们的支持/ sunspot.rb中的太阳黑子设置:

$original_sunspot_session = Sunspot.session
Sunspot.session = Sunspot::Rails::StubSessionProxy.new($original_sunspot_session)

module SolrSpecHelper

  def solr_setup
    unless $sunspot
      $sunspot = Sunspot::Rails::Server.new

      pid = fork do
        STDERR.reopen('/dev/null')
        STDOUT.reopen('/dev/null')
        $sunspot.run
      end
      # shut down the Solr server
      at_exit { Process.kill('TERM', pid) }
      # wait for solr to start
      sleep 5
    end

    Sunspot.session = $original_sunspot_session
  end
end

1 个答案:

答案 0 :(得分:0)

之前我没有遇到过这个确切的问题,但我有两条建议/建议可能会对将来遇到它的人有所帮助。如果您发现成功或不成功,请发表评论。

  1. 虽然不太理想,但你可以从Gemfile的测试组中删除'spring'gem,如果它已被包含在那里。

  2. 尝试运行bundle exec spring binstub --all以“弹出”bin/目录中的可执行文件。

相关问题