Comment vérifier une réponse JSON en utilisant RSpec?

J'ai le code suivant dans mon contrôleur:

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

Dans mon test de contrôleur RSpec, je veux vérifier qu'un certain scénario reçoit une réponse JSON réussie, donc j'ai eu la ligne suivante:

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

Bien que lorsque j'exécute mes tests, j'obtiens l'erreur suivante:

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

Est-ce que je vérifie la réponse de manière incorrecte?

136
demandé sur Fizz 2011-03-01 23:23:29

10 réponses

Vous pouvez examiner l'objet de réponse et vérifier qu'il contient la valeur attendue:

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

Modifier

Changer cela en post le rend un peu plus délicat. Voici une façon de le gérer:

 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

Notez que mock_model ne répondra pas à to_json, donc stub_model ou une instance de modèle réelle est nécessaire.

158
répondu zetetic 2011-03-02 19:27:40

Vous pouvez analyser le corps de réponse comme ceci:

parsed_body = JSON.parse(response.body)

Ensuite, vous pouvez faire vos assertions contre ce contenu analysé.

parsed_body["foo"].should == "bar"
148
répondu brentmc79 2011-03-07 18:25:22

Construire à partir de la réponse de Kevin Trowbridge

response.header['Content-Type'].should include 'application/json'
45
répondu lightyrs 2017-05-23 12:26:10

Il y a aussi la gemme json_spec, qui vaut le détour

Https://github.com/collectiveidea/json_spec

32
répondu acw 2011-08-01 16:44:00

, Simple et facile à moyen pour ce faire.

# 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
10
répondu Chitrank Samaiya 2014-08-10 07:38:56

Une autre approche pour tester juste pour une réponse JSON (pas que le contenu contient une valeur attendue), consiste à analyser la réponse en utilisant ActiveSupport:

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

Si la réponse N'est pas analysable JSON, une exception sera levée et le test échouera.

7
répondu Clinton 2012-11-15 06:01:13

Vous pouvez regarder dans l'en-tête 'Content-Type' pour voir qu'il est correct?

response.header['Content-Type'].should include 'text/javascript'
7
répondu Kevin Trowbridge 2012-12-20 19:29:31

Vous pouvez également définir une fonction d'assistance à l'intérieur de spec/support/

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

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

Et utilisez json_body chaque fois que vous avez besoin d'accéder à la réponse JSON.

Par exemple, dans votre spécification de demande, vous pouvez l'utiliser directement

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
5
répondu Xinyang Li 2017-02-19 14:00:18

Lors de L'utilisation de Rails 5 (actuellement encore en version bêta), il existe une nouvelle méthode, parsed_body sur la réponse de test, qui retournera la réponse analysée comme étant celle à laquelle la dernière requête a été encodée.

Le commit sur GitHub: https://github.com/rails/rails/commit/eee3534b

4
répondu Koen. 2016-03-15 22:42:26

J'ai trouvé un client matcher ici: https://raw.github.com/gist/917903/92d7101f643e07896659f84609c117c4c279dfad/have_content_type.rb

Mettez-le dans spec / support / matchers / have_content_type.rb et assurez-vous de charger des choses à partir du support avec quelque chose comme ça Dans vous spec/spec_helper.rb

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

Voici le code lui-même, juste au cas où il disparaîtrait du lien donné.

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
répondu Zeke Fast 2012-09-28 15:18:37