I have a couple methods defined in controllers that I call in one of the controller actions.
Example from SearchController:
I've been testing these methods by actually making a request. That can't be the most efficient way to test them. I can't exactly call ApplicationController.assigns_session_tags
and then test the result...
controller:
def index
@tag_list = @collection.tags
@tag = @collection.tags.new
end
spec:
describe "GET #index", unit: true do
it "assigns all Tags to @tags" do
tags = %w(Rails Ruby ActiveRecord Haml)
tags.map! { |tag| @collection.tags.create(name: tag) }
mo = Tag.create(name: "Model")
get :index, collection_id: @collection
expect(assigns(:tag_list)).to match_array(tags)
end
error:
1) TagsController GET #index assigns all Tags to @tags
Failure/Error: expect(assigns(:tag_list)).to match_array(tags)
expected collection contained: [#<Tag id: 1, name: "Rails", created_at: "2014-02-05 16:53:30", updated_at: "2014-02-05 16:53:30", collection_id: 1>, #<Tag id: 2, name: "Ruby", created_at: "2014-02-05 16:53:30", updated_at: "2014-02-05 16:53:30", collection_id: 1>, #<Tag id: 3, name: "ActiveRecord", created_at: "2014-02-05 16:53:30", updated_at: "2014-02-05 16:53:30", collection_id: 1>, #<Tag id: 4, name: "Haml", created_at: "2014-02-05 16:53:30", updated_at: "2014-02-05 16:53:30", collection_id: 1>]
actual collection contained: #<ActiveRecord::Associations::CollectionProxy [#<Tag id: 4, name: "Haml", created_at: "2014-02-05 16:53:30", updated_at: "2014-02-05 16:53:30", collection_id: 1>, #<Tag id: 3, name: "ActiveRecord", created_at: "2014-02-05 16:53:30", updated_at: "2014-02-05 16:53:30", collection_id: 1>, #<Tag id: 2, name: "Ruby", created_at: "2014-02-05 16:53:30", updated_at: "2014-02-05 16:53:30", collection_id: 1>, #<Tag id: 1, name: "Rails", created_at: "2014-02-05 16:53:30", updated_at: "2014-02-05 16:53:30", collection_id: 1>, #<Tag id: nil, name: nil, created_at: nil, updated_at: nil, collection_id: 1>]>
the extra elements were: [#<Tag id: nil, name: nil, created_at: nil, updated_at: nil, collection_id: 1>]
my solution:
it "assigns all Tags to @tags" do
ra = @collection.tags.create(name: "Rails")
ru = @collection.tags.create(name: "Ruby")
ac = @collection.tags.create(name: "ActiveRecord")
mo = Tag.create(name: "Model")
get :index, collection_id: @collection
expect(assigns(:tag_list)).to include(ra, ru, ac)
expect(assigns(:tag_list)).not_to include(mo)
end
I could not get around this response in an RSpec controller test.
1) NotesController DELETE #destroy redirects to SearchController#results
Failure/Error: expect(response).to redirect_to results_search_path
Expected response to be a <redirect>, but was <200>
spec:
describe "DELETE #destroy" do
it "removes the tag from the database" do
expect{
delete :destroy, { collection_id: @collection, id: @note }
}.to change(@collection.notes, :count).by(-1)
end
it "redirects to SearchController#results" do
expect(response).to redirect_to results_search_path
end
end
NotesController#destroy
def destroy
@note.destroy
render results_search_path, notice: "Note deleted"
end
See app/controllers/notes_controller.rb
and spec/controllers/notes_controller_spec.rb
.
How would you test that @collection.tags and @collection.notes are functioning properly?
def new
@note = @collection.notes.new
@tag_list = @collection.tags
end
instead of this:
def new
@note = Note.new
@tag_list = Tag.all
end
describe "GET #new", unit: true do
it "assigns a new Note to @note" do
get :new, { collection_id: @collection.id }
expect(assigns(:note)).to be_a_new(Note)
end
it "assigns @note to the current Collection" do
get :new, { collection_id: @collection.id }
expect(assigns(:note).collection).to eq(@collection)
end
it "assigns all tags to @tag_list" do
ra = @collection.tags.create( name: "Rails")
ru = @collection.tags.create( name: "Ruby")
po = @collection.tags.create( name: "Postgres")
get :new, { collection_id: @collection.id }
expect(assigns(:tag_list)).to match_array(@collection.tags)
expect(assigns(:tag_list)).to have(3).items
end
end
I'm not sure if all the files I have in app/models/
should be there. search_handler.rb
, tag_handler.rb
and tag_util.rb
aren't even ActiveRecord models. Those files provide functionality that I didn't want in the AR models. Where should those 3 files live?
Also, I'm not sure about the way in which I built them. I created them to pull query related processes out of my controllers. Originally there was only, tag_handler.rb
. Then, as I was writing search_handler.rb
I realized I was basically going to have to copy some functionality from tag_handler.rb
. Although, looking at things now, the only functionality truly shared by both TagHandler
and SearchHandler
is the parse_tags
method. Anyway, at the time, it seemed that I could pull out some of this Tag
related stuff into a TagUtil
module. It almost seems like I separated things too much. You can see how I use these two classes and module in my NotesController#create
and SearchController#results
actions.
And, how do the app/helpers/
modules play into all this?
I'm having to re-think the structure of the app. Originally I just had Notes, Tags and Taggings models. Taggings, as I'm sure you guess provides me with 'has_many through' associations for both Notes and Tags. Notes have many Tags and Tags have many Notes. Then, I added in User auth with Devise. I could give a User many Notes and many Tags, but I decided I wanted to add in the idea of Collections, groups of related notes. For instance, I don't want notes/tags for programming and notes/tags for leadership hanging out together. That gets messy, especially the tag cloud aspect.
So, I'm currently building the app out so that Users have Collections, and Collections have Notes and Tags. Notes and Tags will be nested resources of Collections. Which brings me to the next question, authorization.
Routes for any user are going to look something like this:
collection_notes GET /collections/:collection_id/notes(.:format) notes#index
POST /collections/:collection_id/notes(.:format) notes#create
new_collection_note GET /collections/:collection_id/notes/new(.:format) notes#new
edit_collection_note GET /collections/:collection_id/notes/:id/edit(.:format) notes#edit
collection_note GET /collections/:collection_id/notes/:id(.:format) notes#show
PATCH /collections/:collection_id/notes/:id(.:format) notes#update
PUT /collections/:collection_id/notes/:id(.:format) notes#update
DELETE /collections/:collection_id/notes/:id(.:format) notes#destroy
collect