I'm new to Ruby (RoR) and rspec.
I have the basic project scaffold and a controller within project/app/controllers/books_controller.rb
Then I'm trying to setup rspec in project/spec/controllers/book_spec.rb
There also exists project/spec/spec_helper.rb that book_spec.rb requires. This file is where I'm trying to include the books_controller.rb file.
How do I include the books_controller.rb file so book_spec.rb has the Book object so when I run rspec, it works?
Currently I've tried things like require_relative "../app/controllers/books_controller" and it doesn't work. It gives me an error like:
uninitialized constant ApplicationController ... in 'require_relative'
Any help would be great.
You don't need to manually include your controllers, just follow the rspec naming conventions and describe the desired object and everything will work fine.
Your controller spec should be named books_controller_spec.rb. That is the exact same name as your controller with a _spec added at the end. This is not a hard requirement but a highly suggested convention as a book_spec.rb really implies a Book model spec rather than a controller.
The critical part is that in your spec you describe the desired object, e.g.
describe BooksController do
#...
end
You might also want to have a look at the rspec-rails gem which provides some useful generators for specs. Ideally you integrate with Rails so whenever you generate a new model or controller a new spec is created for you as well.
Related
I am trying to define some helper methods to be used in the app's controller, but it seems that rails don't even call the controller. just for the test I have the following controller in my app/controllers/my_engine/application_controller.rb and as the documents say rails should find it first and an error should raise because THIS_SHOULD_PRODUCE_ERROR is unknown, but the rspec happily executing without any errors!
class ApplicationController < ActionController::Base
THIS_SHOULD_PRODUCE_ERROR
end
I even tried to mimic the devise's way but the results are the same!
The guide section on the app directory suggests that the application_controller in an engine "will provide any common functionality for the controllers of the engine".
So I wouldn't expect that any additions to that controller will be available to all controllers in an application.
That also means that your application_controller is, I suspect, not getting called when you're running your test. Which would explain why you're not seeing an error.
In terms of how devise does it I think you need to be looking at how define_helpers works. The code you've linked to in your question is the application controller in the test app for the devise gem.
I noticed that I have got things wrong, and the application_controller in the engine does not get applied to application_controller in the app! Also, I couldn't figure out how the devise did it, but I have come up with the simple workaround for this:
require_relative 'controllers/helpers'
module Acu
module Injectors
class << self
ActiveSupport::Notifications.subscribe "start_processing.action_controller" do |**args|
eval((Acu::Configs.get :base_controller).to_s).class_eval do
include Acu::Controllers::Helpers
end
end
end
end
end
This will inject controller helpers to the user's base controller (which I get from the user, default: :ApplicationController) at the end of the class, which is perfect for me (but don't know how to add it to begging of the class if anyone needs it)
I have already read The Basics Of Creating Rails Plugins and several other articles, but I can't find how to supply a model with a gem.
Say, I want to make a gem for tagging (yes, I know about acts_as_taggable_on gem, I need different functionality). So, I want the model Tag to be bundled in the gem. I found no tutorial explaining that.
Of course I tried to reverse-engineer acts_as_taggable_on gem to understand how does it work, but it brought even more confusion: the tutorial I mentioned above says that I should have a dummy app in my gem, in order to test the gem. BUT, acts_as_taggable_on has no such dummy application! How how does it get tested, then?
About the model: ok, I see the file lib/acts_as_taggable_on/tag.rb that seems to be a Tag model:
module ActsAsTaggableOn
class Tag < ::ActiveRecord::Base
# ..........................
end
end
I see that file lib/acts-as-taggable-on.rb requires tag:
require "acts_as_taggable_on/tag"
So I've applied the same approach (assume my plugin is named dftags) :
I have added file lib/dftags/tag.rb:
module Dftags
class Tag < ::ActiveRecord::Base
# attr_accessible :title, :body
end
end
And my lib/dftags.rb looks like this:
module Dftags
end
require "dftags/tag"
I have specs tag_spec.rb:
require 'spec_helper'
describe Tag do
let(:tag) { Tag.new(name: "") }
it { should validate_presence_of :name }
end
And when I run bundle exec rspec spec/, I got error unitialized constant Tag (NameError).
It seems I missed something important. Plus, again, I have dummy app for testing, but acts_as_taggable_on doesn't; so, the testing approach should be different..
So, the questions:
How can I supply a model with gem?
How can I test my gem without dummy app?
Are there some advanced docs about writing rails gems? Actually I tried to check out one more famous gem: devise, but the ruby-fu and rails-fu of the authors is too strong for me to understand it. Where do people learn all of it?
How can I test my gem without dummy app?
The dummy app is only a helper that allows you to use your normal rails testing workflow when building a gem / plugin. You could run the tests without a dummy app but you would need a lot more manual work.
acts_as_taggable_on is pretty much active_record only with the exception of a single helper (as far as I can tell from a quick glance). The author therefore decided that the overhead of maintaining the dummy app was not worth the effort and is setting up active_record by hand. See here https://github.com/mbleigh/acts-as-taggable-on/blob/master/spec/spec_helper.rb#L24 how he establishes the connection to the database.
This code would not be necessary when using a dummy app as rails is taking care of it.
The same is true for the helper. Instead of using the test methods provided by rails he creates a new Class that includes the helper and uses an instance of this class to test it (h/acts-as-taggable-on/blob/master/spec/acts_as_taggable_on/tags_helper_spec.rb#L11).
I added a helper class to my rails project, Foo, at app/helpers/foo.rb. It looks like this.
class Foo
#....stuff
end
I use it in some models, and everything works fine. However, in spec/helpers/foo_helper_spec.rb, I have
require 'spec_helper'
describe Foo do
end
This causes rspec to crash (not report any failed tests, but actually crash), saying
/actionpack-3.2.12/lib/abstract_controller/helpers.rb:153:in `include': wrong argument type Class (expected Module) (TypeError)
If i remove the describe line and just have the file empty, everything works, but I'd like to add some tests soon.
Anyone know how I can fix this?
Thanks.
rails follows the paradigm of convention over configuration. one of those conventions is, that you put modules into the helpers folder, because they get included into your controllers and views.
that's why rspec fails when it tries to include your module, which is actually a class.
if you need to have a real class in there, i think you should put it in a different directory to make it obvious, that this is not a usual rails helper!
for example, if you are implementing some kind of decorator for your views, put it in a decorators folder. if you are implementing some kind of adapter for your model, put it in the models folder or some model subfolder.
I'm writing RSpec tests and I have come to a point where I am not reading the same opinions on different websites. The directory structure for RSpec is clear when we are dealing with spec/controllers and spec/models directories, but the conflicting information I am getting deals with testing views.
I would like to just put these tests in a spec/integration/ directory, but I have read that it's supposed to be under spec/integration, but another book says spec/requests. Does this matter?
To further complicate the situation, I have read conflicting information on naming the actual file names of the tests! For example, if I had a controller/model/view directory called 'people' (I i use haml), I should name the files like this:
spec/integration/people.html.haml_spec.rb
However, another book suggests this:
spec/requests/people_spec.rb
I would like a little explanation of naming conventions within RSpec and WHY I should name the view tests specific names, as well as whether or not it matters to put them under spec/integration or spec/requests. I feel like I'm going with the wind here.
Any help is greatly appreciated.
Check the dates of the books and the versions of RSpec for which they were written. The naming structure has changed slightly over time.
According to the docs for rspec-rails, request specs can go in spec/requests, spec/api, or spec/integration. I prefer to put request specs in spec/requests.
To make things more interesting, if you are using Capybara with rspec-rails, it will work with spec/requests for Capybara 1.x, and spec/features for Capybara 2.
As to individual spec file names, when there is a specific class under test, like a Rails model, you should use an analogous spec file name:
app/models/user.rb -> spec/models/user_spec.rb
View specs should use the template name:
app/views/users/index.html.erb -> spec/views/users/index.html.erb_spec.rb
Namespaced models should include the namespace in the spec file path:
app/models/admin/user.rb -> spec/models/admin/user_spec.rb
The RSpec scaffold generator is a good guide for showing where these specs belong.
When there is no specific class under test, as is the case with request specs, IMHO you should feel free to use a name that describes the thing being tested. E.g. spec/requests/place_an_order_spec.rb.
The files read by the rspec gem are simply those that end in _spec.rb and that are anywhere in the hierarchy below the spec folder as long as the top of the hierarchy is an alpha-numeric word (that is, files under a folder named spec/##/ would not be considered in generating specs.) This is the relevant line of code that implements this, in /gems/rspec-rails-2.14.1/lib/rspec/rails/tasks/rspec.rake:
namespace :spec do
types = begin
dirs = Dir['./spec/**/*_spec.rb'].
map { |f| g=f.sub(/^\.\/(spec\/\w+)\/.*/, '\\1') ; puts ">>> Found #{g}."; g }.
uniq.
select { |f| File.directory?(f) }
Hash[dirs.map { |d| [d.split('/').last, d] }]
end
So all the text in the filename previous to _spec.rb is a convention - it doesn't change how Rails processes the files.
I'd have to read through the code a lot more to figure out what the significance of each folder name, under spec, is - it gets passed through to the Rake spec task that's created, so it's being used for something but I don't know what.
I have a tirable orgnizing my Models in a Sinatra project.
Let's say I have 2 models: Post and Comment, nn Post model, I have to call Comment model. And now I have <class:Post>': uninitialized constant Comment (NameError).
I know its an issue in ordering the requiring for the models, but what about if I have lots of models? What's the Rails way in requiring models, etc.?
UPDATE
I use this code to auto_load my models in Sinatra/Rack/Grape applications. This code should be at the top of your code ie in the boot file.
models = File.join(File.dirname(__FILE__), 'app', 'models') # path to your models
$LOAD_PATH << File.expand_path(models)
# Constent Missing for requiring models files
def Object.const_missing(const)
require const.to_s.underscore
klass = const_get(const)
return klass if klass
end
You should put all of your models in a folder, such as lib in your application, then you can add this to the top of your Sinatra app file:
$: << File.dirname(__FILE__) + "/lib" # Assuming app.rb is at the same level as lib
require 'post'
require 'comment'
You should organise your code so that you do not call other models until all model declarations are loaded.
The Rails way is based on a very nice Ruby feature: const_missing. You could write your const_missing method or looking around the web for a solution with const_missing and sinatra.
no prob when I tried this
a Comment if it is in a method of Post shouldn't be actually evaluated
there must be some circumstance triggering the NameError
don't call Post in the body of the class declaration
load all the model files per the first commenter's suggestion
shouldnt be having the same reference troubles as Java per se
in a dynamic lang like Ruby