আরএসপেক ব্যবহার করে জেএসওএন প্রতিক্রিয়া কীভাবে পরীক্ষা করবেন?


145

আমার নিয়ামকটিতে আমার কাছে নিম্নলিখিত কোড রয়েছে:

format.json { render :json => { 
        :flashcard  => @flashcard,
        :lesson     => @lesson,
        :success    => true
} 

আমার আরএসপেক নিয়ামক পরীক্ষায় আমি যাচাই করতে চাই যে একটি নির্দিষ্ট দৃশ্যে সাফল্য জেসন প্রতিক্রিয়া পেয়েছে তাই আমার নিম্নলিখিত রেখাটি ছিল:

controller.should_receive(:render).with(hash_including(:success => true))

যদিও আমি আমার পরীক্ষা চালানোর সময় আমি নিম্নলিখিত ত্রুটিটি পাই:

Failure/Error: controller.should_receive(:render).with(hash_including(:success => false))
 (#<AnnoController:0x00000002de0560>).render(hash_including(:success=>false))
     expected: 1 time
     received: 0 times

আমি কি ভুলভাবে প্রতিক্রিয়া পরীক্ষা করছি?

উত্তর:


164

আপনি প্রতিক্রিয়া অবজেক্টটি পরীক্ষা করতে পারেন এবং এটিতে প্রত্যাশিত মানটি যাচাই করতে পারেন:

@expected = { 
        :flashcard  => @flashcard,
        :lesson     => @lesson,
        :success    => true
}.to_json
get :action # replace with action name / params as necessary
response.body.should == @expected

সম্পাদনা

postএটিকে পরিবর্তন করে এটিকে কিছুটা জটিল করে তোলে। এটি পরিচালনা করার জন্য এখানে একটি উপায়:

 it "responds with JSON" do
    my_model = stub_model(MyModel,:save=>true)
    MyModel.stub(:new).with({'these' => 'params'}) { my_model }
    post :create, :my_model => {'these' => 'params'}, :format => :json
    response.body.should == my_model.to_json
  end

নোট যা mock_modelপ্রতিক্রিয়া জানাবে না to_json, সুতরাং হয় stub_modelবা একটি বাস্তব মডেল উদাহরণ প্রয়োজন।


1
আমি এটি চেষ্টা করেছি এবং দুর্ভাগ্যক্রমে এটি বলে যে এটি "" এর প্রতিক্রিয়া পেয়েছে। এটি নিয়ামকটিতে কোনও ত্রুটি হতে পারে?
Fizz

এছাড়াও অ্যাকশনটি 'তৈরি করুন', আমি কী পাওয়ার পরিবর্তে কোনও পোস্ট ব্যবহার করি, তার থেকে কিছু যায় আসে?
Fizz

হ্যাঁ, আপনি post :createএকটি বৈধ প্যারামিটার হ্যাশ চাইবেন ।
জেডেটিক

4
আপনার অনুরোধ করা ফর্ম্যাটটিও নির্দিষ্ট করা উচিত। post :create, :format => :json
রবার্ট স্পাইচার

8
জেএসএন হ'ল একটি স্ট্রিং, অক্ষরের ক্রম এবং তাদের ক্রম সম্পর্কিত বিষয়। {"a":"1","b":"2"}এবং {"b":"2","a":"1"}সমান স্ট্রিং নয় যা সমান বস্তুকে চিহ্নিত করে। আপনি স্ট্রিং কিন্তু বস্তুর তুলনা করা উচিত, JSON.parse('{"a":"1","b":"2"}').should == {"a" => "1", "b" => "2"}পরিবর্তে না।
স্কালে

165

আপনি প্রতিক্রিয়া বডিটি এভাবে পার্স করতে পারেন:

parsed_body = JSON.parse(response.body)

তারপরে আপনি সেই বিশ্লেষণকারী সামগ্রীর বিরুদ্ধে নিজের দাবিগুলি তৈরি করতে পারেন।

parsed_body["foo"].should == "bar"

6
এটি অনেক সহজ বলে মনে হচ্ছে । ধন্যবাদ।
tbaums

প্রথম, অনেক ধন্যবাদ। একটি ছোট সংশোধন: JSON.parse (রেসপন্স.বডি) একটি অ্যারে প্রদান করে। ['foo'] তবে একটি হ্যাশ মানটির জন্য একটি কী অনুসন্ধান করে। সংশোধন করা একটি পার্সড_বডি [0] ['ফু']।
CanCeylan

5
JSON.parse কেবল একটি অ্যারে প্রদান করে যদি JSON স্ট্রিংয়ে কোনও অ্যারে থাকে।
redjohn

2
@ প্রিয়ঙ্কাকে যদি এটি এইচটিএমএল ফিরে আসে তবে আপনার প্রতিক্রিয়া জসন নয়। আপনার অনুরোধটি জসন ফর্ম্যাটটি নির্দিষ্ট করে দিচ্ছে তা নিশ্চিত করুন।
brentmc79

10
এছাড়াও আপনি ব্যবহার করতে পারে b = JSON.parse(response.body, symoblize_names: true)যাতে করে আপনি সেগুলিকে তাই মত চিহ্ন ব্যবহার অ্যাক্সেস করতে পারেন যে:b[:foo]
FloatingRock

45

কেভিন ট্রব্রিজের উত্তর বন্ধ করে দেওয়া

response.header['Content-Type'].should include 'application/json'

21
আশা (response.content_type) .to EQ ( "APPLICATION / JSON"): rspec-পাগল এই জন্য একটি মিলকারীর প্রদান করে
ড্যান মাল্য

4
আপনি কেবল Mime::JSONপরিবর্তে ব্যবহার করতে পারবেন না 'application/json'?
ভাসমান

@ ফ্লাইটিংরক আমার মনে হয় আপনার প্রয়োজন হবেMime::JSON.to_s
এডগার অরতেগা


13

এটি করার সহজ এবং সহজ উপায়।

# set some variable on success like :success => true in your controller
controller.rb
render :json => {:success => true, :data => data} # on success

spec_controller.rb
parse_json = JSON(response.body)
parse_json["success"].should == true

11

আপনি ভিতরে কোনও সহায়ক ফাংশন সংজ্ঞায়িত করতে পারেন spec/support/

module ApiHelpers
  def json_body
    JSON.parse(response.body)
  end
end

RSpec.configure do |config| 
  config.include ApiHelpers, type: :request
end

এবং json_bodyযখনই আপনার JSON প্রতিক্রিয়া অ্যাক্সেস করতে প্রয়োজন ব্যবহার করুন।

উদাহরণস্বরূপ, আপনার অনুরোধের ভিতরে আপনি সরাসরি এটি ব্যবহার করতে পারেন spec

context 'when the request contains an authentication header' do
  it 'should return the user info' do
    user  = create(:user)
    get URL, headers: authenticated_header(user)

    expect(response).to have_http_status(:ok)
    expect(response.content_type).to eq('application/vnd.api+json')
    expect(json_body["data"]["attributes"]["email"]).to eq(user.email)
    expect(json_body["data"]["attributes"]["name"]).to eq(user.name)
  end
end

8

কেবল একটি জেএসএন প্রতিক্রিয়ার জন্য পরীক্ষা করার জন্য আরেকটি পদ্ধতির মধ্যে (যে সামগ্রীটির মধ্যে একটি প্রত্যাশিত মান রয়েছে তা নয়), অ্যাক্টিভসপোর্ট ব্যবহার করে প্রতিক্রিয়াটি পার্স করা:

ActiveSupport::JSON.decode(response.body).should_not be_nil

প্রতিক্রিয়া পার্সেবল না হলে JSON একটি ব্যতিক্রম ছুঁড়ে দেওয়া হবে এবং পরীক্ষাটি ব্যর্থ হবে।


7

আপনি 'Content-Type'শিরোনামটি দেখতে পারেন এটি সঠিক কিনা?

response.header['Content-Type'].should include 'text/javascript'

1
কারণ render :json => object, আমি বিশ্বাস করি রেলগুলি 'অ্যাপ্লিকেশন / জেসন' এর একটি সামগ্রী-প্রকারের শিরোনাম প্রদান করে।
লাইটার 21

1
সবচেয়ে ভাল বিকল্প আমি মনে করি:response.header['Content-Type'].should match /json/
bricker

এটি পছন্দ করুন কারণ এটি জিনিসগুলি সহজ রাখে এবং কোনও নতুন নির্ভরতা যুক্ত করে না।
ওয়েবপায়া

5

রেল 5 ব্যবহার করার সময় (বর্তমানে বিটাতে রয়েছে), একটি নতুন পদ্ধতি রয়েছে, parsed_body পরীক্ষার প্রতিক্রিয়াতে যা শেষ অনুরোধটি কীভাবে এনকোড করা হয়েছিল সেটিকে পার্স করা প্রতিক্রিয়া ফিরিয়ে দেবে।

গিটহাবের প্রতিশ্রুতিবদ্ধ: https://github.com/rails/rails/commit/eee3534b


5 টি রেল এটির সাথে বিটাও তৈরি করেছিল #parsed_body। এটি এখনও নথিভুক্ত করা হয়নি তবে কমপক্ষে JSON ফর্ম্যাটটি কাজ করে। নোট করুন যে কীগুলি এখনও স্ট্রিংগুলি রয়েছে (চিহ্নগুলির পরিবর্তে), সুতরাং যে কোনও একটি পেতে পারে #deep_symbolize_keysবা #with_indifferent_accessদরকারী খুঁজে পেতে পারে (আমার পছন্দটি পরে পছন্দ হয়)।
ফ্র্যাংকলিন ইউ

1

আপনি যদি আরএসপেকের দেওয়া হ্যাশ ডিফের সুবিধা নিতে চান তবে শরীরের বিশ্লেষণ করা এবং একটি হ্যাশের সাথে তুলনা করা ভাল। সবচেয়ে সহজ উপায় আমি খুঁজে পেয়েছি:

it 'asserts json body' do
  expected_body = {
    my: 'json',
    hash: 'ok'
  }.stringify_keys

  expect(JSON.parse(response.body)).to eql(expected_body)
end

1

JSON তুলনা সমাধান

একটি পরিষ্কার কিন্তু সম্ভাব্য বৃহত ডিফ ফলন দেয়:

actual = JSON.parse(response.body, symbolize_names: true)
expected = { foo: "bar" }
expect(actual).to eq expected

বাস্তব তথ্য থেকে কনসোল আউটপুট উদাহরণ:

expected: {:story=>{:id=>1, :name=>"The Shire"}}
     got: {:story=>{:id=>1, :name=>"The Shire", :description=>nil, :body=>nil, :number=>1}}

   (compared using ==)

   Diff:
   @@ -1,2 +1,2 @@
   -:story => {:id=>1, :name=>"The Shire"},
   +:story => {:id=>1, :name=>"The Shire", :description=>nil, ...}

(@ ফ্লোটারক দ্বারা মন্তব্য করার জন্য ধন্যবাদ)

স্ট্রিং তুলনা সমাধান

যদি আপনি একটি লোহা-পরিহিত সমাধান চান তবে আপনার এমন পার্সার ব্যবহার এড়ানো উচিত যা মিথ্যা ইতিবাচক সাম্যের পরিচয় দিতে পারে; একটি স্ট্রিং বিরুদ্ধে প্রতিক্রিয়া বডি তুলনা করুন। উদাহরণ:

actual = response.body
expected = ({ foo: "bar" }).to_json
expect(actual).to eq expected

তবে এই দ্বিতীয় সমাধানটি কম দৃষ্টিভঙ্গিযোগ্য কারণ এটি সিরিয়ালযুক্ত জেএসওএন ব্যবহার করে যাতে প্রচুর পালানো উদ্ধৃতি চিহ্ন অন্তর্ভুক্ত থাকে।

কাস্টম ম্যাচার সমাধান

আমি নিজেকে একটি কাস্টম ম্যাচার লেখার প্রবণতা লিখি যা জিনস পাথগুলি পৃথকভাবে পুনরাবৃত্ত করার জন্য সুনির্দিষ্টভাবে পিনপয়েন্ট করার আরও ভাল কাজ করে। আপনার আরএসপেক ম্যাক্রোগুলিতে নিম্নলিখিতগুলি যুক্ত করুন:

def expect_response(actual, expected_status, expected_body = nil)
  expect(response).to have_http_status(expected_status)
  if expected_body
    body = JSON.parse(actual.body, symbolize_names: true)
    expect_json_eq(body, expected_body)
  end
end

def expect_json_eq(actual, expected, path = "")
  expect(actual.class).to eq(expected.class), "Type mismatch at path: #{path}"
  if expected.class == Hash
    expect(actual.keys).to match_array(expected.keys), "Keys mismatch at path: #{path}"
    expected.keys.each do |key|
      expect_json_eq(actual[key], expected[key], "#{path}/:#{key}")
    end
  elsif expected.class == Array
    expected.each_with_index do |e, index|
      expect_json_eq(actual[index], expected[index], "#{path}[#{index}]")
    end
  else
    expect(actual).to eq(expected), "Type #{expected.class} expected #{expected.inspect} but got #{actual.inspect} at path: #{path}"
  end
end

ব্যবহারের উদাহরণ 1:

expect_response(response, :no_content)

ব্যবহারের উদাহরণ 2:

expect_response(response, :ok, {
  story: {
    id: 1,
    name: "Shire Burning",
    revisions: [ ... ],
  }
})

উদাহরণ আউটপুট:

Type String expected "Shire Burning" but got "Shire Burnin" at path: /:story/:name

নেস্টেড অ্যারেতে গভীরতা না দেখানোর জন্য আর একটি উদাহরণ আউটপুট:

Type Integer expected 2 but got 1 at path: /:story/:revisions[0]/:version

আপনি দেখতে পাচ্ছেন, আউটপুট আপনাকে ঠিক আপনার প্রত্যাশিত JSON ঠিক করতে কোথায় তা বলবে।


0

আমি এখানে একটি গ্রাহক ম্যাচারের সন্ধান পেয়েছি: https://raw.github.com/gist/917903/92d7101f643e07896659f84609c117c4c279dfad/have_content_type.rb

এটিকে অনুমান / সমর্থন / ম্যাথারস / হ্যাভ কনটেন্ট_টাইপ.আরবিতে রাখুন এবং আপনার পক্ষে স্পেস / স্পেস_হেল্পার.আরবি এ জাতীয় কিছু দিয়ে সমর্থন থেকে স্টাফ লোড করার বিষয়টি নিশ্চিত করুন make

Dir[Rails.root.join('spec/support/**/*.rb')].each {|f| require f}

এখানে কোডটি নিজেই দেওয়া আছে, কেবলমাত্র প্রদত্ত লিঙ্কটি থেকে এটি অদৃশ্য হয়ে গেলে।

RSpec::Matchers.define :have_content_type do |content_type|
  CONTENT_HEADER_MATCHER = /^(.*?)(?:; charset=(.*))?$/

  chain :with_charset do |charset|
    @charset = charset
  end

  match do |response|
    _, content, charset = *content_type_header.match(CONTENT_HEADER_MATCHER).to_a

    if @charset
      @charset == charset && content == content_type
    else
      content == content_type
    end
  end

  failure_message_for_should do |response|
    if @charset
      "Content type #{content_type_header.inspect} should match #{content_type.inspect} with charset #{@charset}"
    else
      "Content type #{content_type_header.inspect} should match #{content_type.inspect}"
    end
  end

  failure_message_for_should_not do |model|
    if @charset
      "Content type #{content_type_header.inspect} should not match #{content_type.inspect} with charset #{@charset}"
    else
      "Content type #{content_type_header.inspect} should not match #{content_type.inspect}"
    end
  end

  def content_type_header
    response.headers['Content-Type']
  end
end

0

উপরের উত্তরগুলির অনেকগুলি পুরোনো পুরানো, সুতরাং এটি আরএসপেকের একটি সাম্প্রতিক সংস্করণ (3.8+) এর জন্য একটি দ্রুত সংক্ষিপ্তসার। এই সমাধানটি রুবোকপ-আরএসপেক থেকে কোনও সতর্কতা উত্থাপন করে না এবং এটি আরএসপেকের সেরা অনুশীলনের সাথে ইনলাইন রয়েছে :

একটি সফল JSON প্রতিক্রিয়া দুটি জিনিস দ্বারা চিহ্নিত করা হয়:

  1. প্রতিক্রিয়াটির বিষয়বস্তু হ'ল application/json
  2. প্রতিক্রিয়াটির শরীরে ত্রুটি ছাড়াই পার্স করা যায়

অনুমান যে প্রতিক্রিয়া অবজেক্টটি পরীক্ষার বেনামেয় বিষয়, উপরের দুটি শর্তই ম্যাচচার্সে আরএসপেকের অন্তর্নির্মিত ব্যবহার করে বৈধ হতে পারে:

context 'when response is received' do
  subject { response }

  # check for a successful JSON response
  it { is_expected.to have_attributes(content_type: include('application/json')) }
  it { is_expected.to have_attributes(body: satisfy { |v| JSON.parse(v) }) }

  # validates OP's condition
  it { is_expected.to satisfy { |v| JSON.parse(v.body).key?('success') }
  it { is_expected.to satisfy { |v| JSON.parse(v.body)['success'] == true }
end

আপনি যদি নিজের বিষয়ের নামকরণের জন্য প্রস্তুত থাকেন তবে উপরের পরীক্ষাগুলি আরও সরল করা যেতে পারে:

context 'when response is received' do
  subject(:response) { response }

  it 'responds with a valid content type' do
    expect(response.content_type).to include('application/json')
  end

  it 'responds with a valid json object' do
    expect { JSON.parse(response.body) }.not_to raise_error
  end

  it 'validates OPs condition' do
    expect(JSON.parse(response.body, symoblize_names: true))
      .to include(success: true)
  end
end
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.