smart | Webentwicklung
Alles rund um HTML5, PHP, WordPress & Co.

Ruby on Rails & RSpec: JSON-Response testen

28. Juni 2012
Stephan
Ruby on Rails: JSON-Response mit RSpec testen

Zurzeit arbeite ich gerade an einer Ruby on Rails Applikation, bei welcher ich auf der Clientseite das JavaScript-Framework backbone.js einsetze.

Dabei ist es so, dass ich per backbone.js hauptsächlich asynchrone Anfragen (AJAX-Requests) an den Server sende. Die zu übertragenden Daten werden dabei im JSON-Format übermittelt. Analog dazu antwortet der Server auf die jeweiligen Anfragen natürlich auch mit einer asynchronen JSON-Antwort (AJAX-Response).

Im 7. Teil des Tutorials „Todo-App mit backbone.js & CoffeeScript erstellen“ findet ihr das eben erwähnte noch einmal ausführlich erklärt.

In diesem Artikel möchte ich euch zeigen, wie ihr die im Rails-Controller definierten Antworten mittels RSpec testen könnt.

Beispiel-Controller erstellen

Wir nehmen an, wir haben eine Todo-Liste und der Benutzer kann mit der Applikation seine Todos verwalten. Der Benutzer kann also Todos anlegen, bearbeiten, löschen und natürlich sich alle Todos anzeigen lassen.

Wie das nun alles auf der Clientseite umgesetzt ist, soll uns hier egal sein. Was ür uns interessant ist, ist der dazugehörige Controller auf der Serverseite, den wir einfach TodosController nennen.

Dabei könnte unser TodoController z.B. so implementiert sein:

class TodosController < ApplicationController
    def index
        @todos = Todo.all

        respond_to do |format|
            format.html # index.html.erb
            format.json { render json: @todos }
        end
    end

    def create
        @todo = Todo.new(params[:todo])

        if @todo.save
            render json: @todo
        else
            render :json => @todo.errors, :status => :unprocessable_entity
        end
    end

    def update
        @todo = Todo.find(params[:id])

        if @todo.update_attributes(params[:todo])
            render json: @todo
        else 
            render :json => @todo.errors, :status => :unprocessable_entity
        end
    end

    def destroy
        @todo = Todo.find(params[:id])
        @todo.destroy

        render :json => {:todo => @todo, :notice => 'Erfolgreich gelöscht'}
    end
end

Ich denke der Code ist relativ leicht zu verstehen, aber falls ihr Fragen dazu habt, schreibt doch einfach ein Kommentar.

RSpec-Tests erstellen

Nachdem ihr in eurem specs/controllers-Verzeichnis die Spec-Datei todos_controller_spec.rb angelegt habt, können wir auch schon mit dem Testen beginnen.

Ich werde im Folgenden aber nicht alle Tests für den Controller aufführen, sondern nur, wie der Titel des Artikels schon vermusten lässt, die JSON-Responses der einzelnen Methoden.

Bevor wir nun wirklich anfangen, erstellen wir uns im specs/spec_helpers-Verzeichnis noch eine Datei namens todos_spec_helper.rb mit folgendem Inhalt:

module TodosSpecHelper
    def mock_todo(stubs = {})
        @mock_todo ||= mock_model(Todo, stubs).as_null_object
    end

    def valid_todo_attributes
        {
            'done' => true,
            'title' => 'Do not forget to call John!'
        }
    end
end

Jetzt können wir anfangen in der todos_controller_spec.rb-Datei unsere Tests zu schreiben. Als erstes die index-Methode bei der wir darauf achten müssen, dass diese sowohl HTML- als auch JSON Anfragen bearbeiten kann.

Deshalb werde ich hier beide Response-Tests zum Vergleichen angeben:

describe '#GET index' do
    context 'when html repsonse requested'
        it 'should render the index page' do
            get :index
            response.should render_template :index
        end
    end

    context 'when json repsonse requested'
        it 'should render all todos' do
            Todo.stub(:all).and_return [mock_todo(:as_json => valid_todo_attributes)]
            get :index, :format => :json
            JSON.parse(response.body).should == [mock_todo.as_json]        
        end
    end
end

Jetzt die Tests für die create-Methode:

describe '#POST create' do
    context 'when saved successfully' do
        it 'should render the created todo' do
            Todo.stub(:new).and_return mock_todo(:as_json => valid_todo_attributes)
            post :create, :todo => {}
            JSON.parse(response.body).should == mock_todo.as_json
        end
    end

    context 'when not saved successfully' do
        before(:each) do
            Todo.stub(:new).and_return mock_todo(:save => false)
        end

        it 'should send the response status unprocessable_entity (http code 422)' do
            post :create, :todo => {}
            response.status.should == 422
        end

        it 'should render the todo errors' do
            mock_todo.stub(:errors).and_return({:any => 'error'})
            post :create, :todo => {}
            JSON.parse(response.body).should == {:any => 'error'}.as_json
        end
    end
end

Weiter gehts mit update:

describe '#PUT update' do
    context 'when saved successfully' do
        it 'should render the created todo' do
            Todo.stub(:find).with('20').and_return mock_todo(:as_json => valid_todo_attributes)
            post :update, :id => '20'
            JSON.parse(response.body).should == mock_todo.as_json
        end
    end

    context 'when not saved successfully' do
        before(:each) do
            Todo.stub(:find).with('20').and_return mock_todo(:update_attributes => false)
        end

        it 'should send the response status unprocessable_entity (http code 422)' do
            post :update, :id => '20'
            response.status.should == 422
        end

        it 'should render the todo errors' do
            mock_todo.stub(:errors).and_return({:any => 'error'})
            post :update, :id => '20'
            JSON.parse(response.body).should == {:any => 'error'}.as_json
        end
    end
end

Zu guter Letzt die delete-Methode:

describe '#DELETE destroy' do
    before(:each) do
        Todo.stub(:find).with('20').and_return mock_todo(:as_json => valid_todo_attributes)
    end

    it 'should render the deleted todo' do        
        delete :destroy, :id => '20'
        JSON.parse(response.body)['todo'].should == mock_todo.as_json
    end

    it 'should render a success notice' do
        delete :destroy, :id => '20'
        JSON.parse(response.body)['notice'].should == 'Erfolgreich gelöscht'
    end
end

Fazit

Das Testen von JSON-Responses mit RSpec ist eigentlich relativ einfach, wenn man denn endlich rausgefunden hat wie es geht.

Ich hoffe also, dass ich einigen von euch, die auch Probleme mit dem Testen von JSON-Responses haben, mit diesem Artikel helfen konnte.

Bei weiteren Fragen oder Unklarheiten schreibt doch einfach ein Kommentar. 🙂

Kommentare  
0 Kommentare vorhanden
0 Trackbacks/Pingbacks vorhanden
Du bist herzlich eingeladen auch ein Kommentar zu hinterlassen!
Kommentar schreiben

Vielen Dank für dein Kommentar!