Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

Service Class

# services/search.rb

class Search
  include Rails.application.routes.url_helpers
  include ActionView::Helpers::TagHelper

  def initialize(term, app_ids)
    @term = term
    @app_ids = app_ids
  end

  def results
    term = @term
    app_ids = @app_ids

    multi_search = Tire.multi_search do
      no_app_indices = [Event, PastEvent, Review].map {|c| c.tire.index_name}
      search :without_app, indices: no_app_indices do
        query { string term }
      end

      app_indices = [Question, Answer, Link, ServiceProvider].map {|c| c.tire.index_name}
      search :with_app, indices: indices do
        query do
          filtered do
            query { string term }
            filter :terms, app_id: app_ids
          end
        end
      end
    end
    multi_search.results[:with_app].to_a + multi_search.results[:without_app].to_a
  end

  def autocomplete
    results.map do |result|
      title, caption, route = set_item_based_on_type(result)
      {title: strip_tags(title), label: set_label(strip_tags(title), caption), value: route}
    end
  end

  def strip_tags(title)
    title.gsub(%r{</?[^>]+?>}, '').strip
  end

Specs

describe Search do
  before(:all) do
    Question.index.delete
    Question.tire.create_elasticsearch_index

    @question1 = create :question, content: "Some super question?"
    @question2 = create :question, content: "Some extra question?"

    app = create :app, name: "Marketing", id: 76
    @question1 = create :question, content: "Some super question?", app: app
    @question2 = create :question, content: "Some extra question?", app: app

    # wait for indexing to finish
    sleep 1
  end

  describe "#results" do
    it "returns matched results" do
      Search.new("Some", [76]).results.map(&:content).should == ["Some super question?", "Some extra question?"]
    end

    it "excludes not matching results" do
      Search.new("extra", [76]).results.map(&:content).should == ["Some extra question?"]
    end

    it "returns no matches" do
      Search.new("hiper", [76]).results.map(&:content).should == []
    end

    it "should filter results by app" do
      Search.new("some", [77]).results.map(&:content).should == []
    end
  end

  describe "#autocomplete" do
    it "returns array with matching items" do
      Search.new("super", [76]).autocomplete.should ==  
        [{
          :title=>@question1.content,
          :label=>"Some <span class=\"bold\">super</span> question? <span class=\"search-type\">Question</span>",
          :value=>"/Marketing/q/#{@question1.id}"
        }]
    end
  end

  describe "#strip_tags" do
    it "should strip title" do
      Search.new("super", [76]).strip_tags("<p>Some text</p> ").should == "Some text"
    end
  end

Is there a better way to test that methods in service class ?

share|improve this question
add comment

1 Answer

up vote 2 down vote accepted

Use let (or let!) to set up your fixtures instead of the instance variables.

let!(:app)        { create :app, name: "Marketing", id: 76 }
let!(:question1)  { create :question, content: "Some super question?" }
let!(:question2)  { create :question, content: "Some extra question?" }
let!(:with_app1)  { create :question, content: "Some super question?", app: app }
let!(:with_app2)  { create :question, content: "Some extra question?", app: app }

Use subject (in combination with let). Here are some examples:

describe "#results" do
  subject { Search.new(keyword, [app_id]).results.map(&:content) }

  context "existing app_id" do
    let(:app_id) { 76 }

    context "multiple matches" do
      let(:keyword) { "Some" }

      its(:size) { should == 2 }
      it { should include(with_app1.content) }
      it { should include(with_app2.content) }
    end

    context "single matches" do
      let(:keyword) { "extra" }

      its(:size) { should == 1 }
      it { should include(with_app2.content) }
    end

    context "no matches" do
      let(:keyword) { "hiper" }

      it { should be_empty }
    end
  end

  context "non-exisiting app_id" do
    let(:keyword) { "some" }
    let(:app_id)  { 123 }

    it { should be_empty }
  end
end

You will end up with more code if you follow this through, but also with more and finer grained examples (each it or its block is a single example).

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.