2

I'm building an API where Users can follow and be followed by other Users. I have created a Relationship table to handle the associations along with Relationship endpoints. Here is my endpoint for following someone:

def create
 user = User.find(params[:followed_id])
 if user
   current_user.follow(user)
   render json: [current_user, Relationship.where(followed_id: user.id, follower_id: current_user.id)], status: 201, location: [:api, current_user]
 else
   render json: { errors: current_user.errors }, status: 422
 end
end

Everything is working as intended. As you can see, I want to respond with my current_user object and the newly created Relationship object. Both of which work when I hit this endpoint providing an Authorization token and the id of the user I want to follow. The issue that I am having is testing that I get back a Relationship object. Here are the tests I have for the following endpoint:

describe "POST #create" do

 context "When relationship is successfully created" do
   before(:each) do
     @user = FactoryGirl.create(:user)
     other_user = FactoryGirl.create(:user)
     api_authorization_header(@user.auth_token)
     post :create, followed_id: other_user.id
   end

   it "should respond w/ current_user object" do
     user_response = JSON.parse(response.body, symbolize_names: true)
     expect(user_response[0][:id]).to eq(@user.id)
   end

### THIS TEST IS FAILING & THE ONE THAT I NEED HELP WITH.
   it "should respond w/ created relationship object" do
     user_response = JSON.parse(response.body, symbolize_names: true)
     expect(user_response[1]).to be_kind_of(Relationship)
   end

   it { should respond_with(201) }
 end

end

Here are some outputs of user_response:

user_response = {:id=>1233, :email=>"[email protected]", :first_name=>"Marcelina", :last_name=>"Morissette", :created_at=>"2016-03-28T18:16:09.875Z", :updated_at=>"2016-03-28T18:16:09.875Z", :auth_token=>"Ky25sYoJc4gH-p122yEH"}
{:id=>150, :follower_id=>1233, :followed_id=>1234, :created_at=>"2016-03-28T18:16:09.892Z", :updated_at=>"2016-03-28T18:16:09.892Z"}

You can see user_response is returning an array of the two objects I asked it to respond to.

user_response[1] = {:id=>150, :follower_id=>1233, :followed_id=>1234, :created_at=>"2016-03-28T18:16:09.892Z", :updated_at=>"2016-03-28T18:16:09.892Z"}

Here is there error I receive when trying to run `expect(user_response[1]).to be_kind_of(Relationship)

1) Api::V1::RelationshipController POST #create When relationship is successfully created should respond w/ created relationship object
 Failure/Error: expect(user_response[1]).to be_kind_of(Relationship)
   expected [{:id=>153, :follower_id=>1239, :followed_id=>1240, :created_at=>"2016-03-28T18:18:43.064Z", :updated_at=>"2016-03-28T18:18:43.064Z"}] to be a kind of Relationship(id: integer, follower_id: integer, followed_id: integer, created_at: datetime, updated_at: datetime)
 # ./spec/controllers/api/v1/relationship_controller_spec.rb:27:in `block (4 levels) in <top (required)>'

Any suggestions on how I can test that the second object being sent back is an object from the Relationship class? Also, if there is a more 'correct' way for handling this endpoint, I would appreciate the lesson =).

1 Answer 1

1

"create" method is returning a JSON response, not an ActiveRecord Object. An HTTP response can't be "be_kind_of(Relationship)". JSON is just formatted text.

If you are building a RESTful API you must be testing the JSON responses and the HTTP status in the requests, and not testing the controllers. Something like:

it 'updates a user on update_user action' do
  params = {
    id:     @user.id,  # FactoryGirl object
    fname: 'John',
    lname: 'Smith',
    email: '[email protected]'
  }

  post '/v1/users/update', params, @env

  # test for the 200 status-code
  expect(response).to be_success
  expect(json['data']['email']).to eq '[email protected]'
end   

BTW, you shouldn't be using sessions in your API. Instead send your user id as a param in your request.

http://matthewlehner.net/rails-api-testing-guidelines/

Sign up to request clarification or add additional context in comments.

3 Comments

Okay, that makes sense. I just changed it to check the follower_id against the @user id.
In regards to not using sessions in your API. The link that you shared also uses sessions in their API demonstration. Which is what I do as well to return an auth_token on successful logins. Other examples/tutorials that I have referenced all handle user logins through a 'stateless' session that returns an authentication token. Is there a better way to handle this?
I like to send all the data the function needs as a JSON object because this way is more explicit and easy to set in the documentation: api.yourcompany.com/v1/crateuser{token_number:'3453xf343', token_secret: '*****', user_id: 56, follower_id: 36} note the "https" protocol. The user sends the toek and the token_secret in all the calls.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.