How do I customise test generators in default Rails? - ruby-on-rails

Edit: Originally this was a Rails 5.2 question but it still seems relevant with Rails 6 and 6.1
I am trying to customise which tests are generated by default in a vanilla Rails 5.2.x app. I don't seem to be able to configure the Minitest generators though. The following code in my config/application.rb causes a error minitest [not found] error when I try and generate a model.
config.generators do |generate|
generate.test_framework :minitest, model: false
end
My Assumptions/Understanding
In Rails 5.2 the default test frameworks are Minitest and Capybara
Minitest gem is included automatically with rails new
You can customise generator behaviour with a config.generators do block in config/application.rb
test_framework "defines which test framework to use. Defaults to false and will use Minitest by default"
Clearly there's a gap in my understanding or one of my assumptions is wrong.
Steps to Reproduce
rails -v
# Rails 5.2.1.1
rails new testing-test-frameworks
# Bundle install outputs: 'Using minitest 5.11.3'
cd testing-test-frameworks
rails g model --help
# Default [test framework]: test_unit [why???]
rails g model person
# invoke test_unit [why???]
# create test/models/person_test.rb
# create test/fixtures/people.yml
Strangely enough, the official Rails testing guide also has lots of references to invoke test_unit for both unit tests and system tests.
Other Things I Tried
I tried other settings in the config.generators block just to make sure it's working
Given that the test_unit generators seem to be non-negotiable I tried changing the generations through test_unit; config.generators.test_framework :test_unit, model: false
I tried to disable various other tests; fixture:false, integration: false, model: false, spec: false, system: false, view: false
I tried to confirm I don't have any other gems or settings that might be messing with things (several similar sounding problems related to an old version of FactoryBot/Girl)
I tried variations such as generate.test_framework :mini_test mentioned in other answers
Questions
How do I control these generators?
Where (on the Internet or on my filesystem) do I go to learn about the valid options that can be passed to the generators for each test framework?
Why does the help system in my vanilla app say the default test generator is TestUnit when the official documentation says it's Minitest?

I'm attempting to partially answer some of my own questions (2+ years later) based on recent learnings (and based on Rails 6). If I'm wrong, hopefully Cunningham's Law holds true.
1. :test_unit == :minitest (more or less)
The first big point of confusion for me was that Minitest is indeed the default test framework under the hood but just about everything within Rails (still?) seems to refer to TestUnit/test_unit.
Examples include:
rails g model --help output (snippets below)
-t, [--test-framework=NAME] # Test framework to be invoked
# Default: test_unit
^^^^^^^^^
TestUnit options:
^^^^^^^^
rails g scaffold Example output (snippets below):
create app/models/example.rb
invoke test_unit
^^^^^^^^^
create test/models/example_test.rb
invoke scaffold_controller
create app/controllers/examples_controller.rb
invoke test_unit
^^^^^^^^^
create test/controllers/examples_controller_test.rb
create app/helpers/examples_helper.rb
invoke test_unit
^^^^^^^^^
The way we generate tests:
bin/rails generate test_unit:model article title:string body:text
^^^^^^^^^
2. Digging into Test Unit
Sure enough, if we look in all.rb we see that "Test Unit" is being included via the test_unit Railtie. If (like me) you don't really know what a Railtie is, check out this blog post, I found it quite helpful.
In terms of what options we can provide in our config/application.rb config.generators block, I think the only options we can pass with config.generators.test_framework :test_unit are fixture: true/false (and maybe specifying a fixture_replacement path(?), based on the apparent defaults in the test_unit railtie).
3. What else can "TestUnit" generate?
If you run rails g to get a list of available generators, only four TestUnit ones are mentioned in the output:
TestUnit:
test_unit:channel
test_unit:generator
test_unit:mailbox
test_unit:plugin
But the Rails Testing Guide mentions test_unit:model and test_unit:scaffold as well. And when I tried rails g test_unit:foo Rails suggested perhaps I meant rails g test_unit:job so that's three "undocumented" generators.
From what I can tell, those three extras come from this generators folder, along with a few others. If so, I think the complete list is:
test_unit:channel *not in that folder, seems to be added by
ActionCable
test_unit:controller
test_unit:generator
test_unit:helper
test_unit:integration
test_unit:job
test_unit:mailbox *not in that folder, seems to be added by ActionMailbox
test_unit:mailer
test_unit:model
test_unit:plugin
test_unit:scaffold
test_unit:system

Related

Having default Rails generators call a custom generator

To be clear, here's NOT what I'm trying to:
Have my custom generator call a default Rails generator
Replace a default Rails generator with my own
What I want to do is have my generator be invoked automatically when I call:
rails generate scaffold User name age:integer
I'm not writing a test replacement or anything, it's completely custom. All information I find about generators out there involve one of those first two cases but not what I want to do. As soon as I found hook_for I immediately thought that was exactly what I needed, but it appears to do the opposite -- invokes another Rails generator from inside of my custom one (if I wanted a test file created for my custom generator I'd call hook_for :test_framework and then define a TestUnit::MyCustomGenerator class somewhere).
I suppose I could monkey patch the default scaffold generator to call mine but that feels dirty. I've looked into some gems that do something similar like https://github.com/Skalar/i18n-yaml-generator but trying to convert that to use an initializer and lib/generators isn't working for me. The scaffold_generator runs but mine never gets called.
for me it works from lib/generators/
$ rails g generator scaffold
create lib/generators/scaffold
create lib/generators/scaffold/scaffold_generator.rb
create lib/generators/scaffold/USAGE
create lib/generators/scaffold/templates
$ rails g scaffold
Usage:
rails generate scaffold NAME [options]
....
what/will/it/create
http://guides.rubyonrails.org/generators.html#generators-lookup
another way may be :)
fork railties gem
override Rails::Generators::ScaffoldGenerator class to your liking
install locally or specify source path in Gemfile
also if 'its completely custom', it is just fair to call it a different name, no?

Rails 2.3 and minitest

I would like to try minitest with an existing Rails 2.3.14 application. I already tried several approaches but none of them seem to work.
It essentially boils down to this. When I add minitest to the Gemfile, all tests I run with bundle exec rake test are NOT executed with minitest.
The problem is that most of the test cases extend the ActiveSupport::Testcase or ActionController::Testcase (which extends ActiveSupport::TestCase).
By looking at the code of this class I saw that it already (somehow) suppports MiniTest:
class TestCase < ::Test::Unit::TestCase
if defined? MiniTest
Assertion = MiniTest::Assertion
alias_method :method_name, :name if method_defined? :name
alias_method :method_name, :__name__ if method_defined? :__name__
else
...
end
(see https://github.com/rails/rails/blob/2-3-stable/activesupport/lib/active_support/test_case.rb for complete code)
My question here is:
Is it somehow possible to use minitest instead of test::unit in Rails 2.3 without having to extend (MiniTest::Unit::TestCase)
Any testing framework is made of two things, a way to define and run tests (call this the test runner) and an assertion framework.
Looking at ActiveSupport it seems that if MiniTest is defined at all, running the rails Unit test runner will default to using MiniTest's assertions classes. This means you get to use Minitest's assertion syntax.
Getting MiniTest defined should just be a case of requiring 'minitest\unit'.
However if you want to use the runners, then you need to derive from the MniTest framework test fixture base classes.
There is a step by step walkthrough in railscasts for setting it up yourself.
It boils down to:
Remove test_unit from your config/applications.rb
Add the minitest gem to your gem file "Gemfile"
Setup the environment in a helper file you can include into your tests
Add a rake task to run the tests. (/lib/tasks/minitest.rake)
Railscasts has the details.
If you don't want to change your existing tests to do that.. then you have to monkey patch Test::Unit to redefine TestCase to either be the chosen MiniTest base class or something that derives from one. This is the approach that this gem takes and may give you some ideas on how best to go about that.
The other solution is to do a search/replace through your code to switch the base classes of the test cases.
I hope this helps.
I Updated my answer and remove the following as out of date:
minitest-rails has rightly been pointed out as it works for Rails 3.1 only.
This gem seems to be old (26th March 2009) but it may help. Check the source code if you need hints to do something similar.

Rails 3 Inflection Problem

I'm having a problem with generating scaffold for a Regatta. When I run
rails g scaffold Regatta name:string start_date:datetime
I get a model named regattum and a controller called regatta_controller (instead of regattas_controller)
invoke active_record
create db/migrate/20110609221608_create_regatta.rb
create app/models/regattum.rb
invoke test_unit
create test/unit/regattum_test.rb
create test/fixtures/regatta.yml
route resources :regatta
invoke scaffold_controller
create app/controllers/regatta_controller.rb
invoke erb
create app/views/regatta
create app/views/regatta/index.html.erb
create app/views/regatta/edit.html.erb
create app/views/regatta/show.html.erb
create app/views/regatta/new.html.erb
create app/views/regatta/_form.html.erb
invoke test_unit
create test/functional/regatta_controller_test.rb
invoke helper
create app/helpers/regatta_helper.rb
invoke test_unit
create test/unit/helpers/regatta_helper_test.rb
invoke stylesheets
identical public/stylesheets/scaffold.css
Obviously this is an inflection problem, but whenever I modify /config/initializers/inflections.rb I get an error saying:
The name 'Regatta' is either already used in your application or reserved by Ruby on Rails. Please choose an alternative and run this generator again.
I have tried everything I can think of to make it work, but I keep getting an error. Any advice on a solution or a workaround would be greatly appreciated!
Update
Here are some of the things I have tried:
ActiveSupport::Inflector.inflections do |inflect|
inflect.irregular 'regatta', 'regattas'
end
That didn't change anything so I tried a few of the options below, among others,in varying combinations to no avail:
inflect.plural 'regatta', 'regattas'
inflect.singular 'regattas', 'regatta'
inflect.singular 'regatta', 'regatta'
Update 2
Here is the code I used in inflections.rb once I figured out what I was doing wrong:
ActiveSupport::Inflector.inflections do |inflect|
inflect.plural 'regatta', 'regattas'
inflect.singular 'regatta', 'regatta'
inflect.singular 'regattas', 'regatta'
end
Hopefully this will help someone out in the future!
I recognize this post is old, but I thought I would contribute my findings as well.
I have been integrating rpsec and cucumber into my application after much development (yes; naughty, naughty). I wanted to generate the rspec stubs for my models and get my application unit tested. I am usually able to move an associated migration out of db/migrate/ and run 'bundle exec rails g ' to regenerate the scaffolds and in this case, the rspec stubs. Not this time.
<terminal>
bundle exec rails g scaffold User email:string ...
updated_at:datetime roles_mask:integer --trace
invoke active_record
The name 'User' is either already used in your application
or reserved by Ruby on Rails. Please choose an alternative
and run this generator again.
My problem was resolved by temporarily commenting out the 'devise_for' line in my 'config/routes.rb' file
config/routes.rb
#devise_for :users
Bingo. And this isn't the only time that Devise has surprised me with 'black magic' that is not readily apparent. I un-commented the line once my rspec stubs were generated, of course. Now it's off to writing my unit tests!
Given the error message saying that Regatta is already in use in your application (it's obviously not reserved by Rails), I'm guessing the Regattum model is still in place.
I'd recommend destroying the scaffold and trying again. It sounds like, once a word is already defined in the application, Rails has a built-in protection against changing the inflection. So, this error being thrown is to protect against unexpected behavior in this very situation.

How to get generators call other generators in rails 3

I am experimenting with gem development, right now specifically generators. So far I have successfully created two generators that do their job just perfectly. These two generators are in the same directory.
However, right now I have to call each of them separately.
What I'd like to do is just call one generator and have that generator call all the other ones. Just would type
rails g generator_name
and this would call x other generators.
Does anyone know how would I got about doing this?
Help is much appreciated, thanks!
In your generator, you can just call
generate "some:generator" # can be anything listed by 'rails g'
for example:
module MyGem
class InstallGenerator < Rails::Generators::Base
def run_other_generators
generate "jquery:install" # or whatever you want here
end
end
end
By the way, if you are working on Rails 3 gems, this question can also help out:
Rails 3 generators in gem
Another possibility is to use something like
invoke 'active_record:model', 'foo bar:string baz:float'
which is not as clean as generate, but has one advantage: When your generator gets called via rails destroy, this call -- like may other of Thors actions -- will try to revoke the action of the generator you invoke.
There's a catch however: Probably due to Thors dependency management, this only works once per generator you want to call, meaning that a second invoke of the same generator will do nothing. This can be circumvented by using a statement like
Rails::Generators.invoke 'active_record:model', '...', behavior: behavior
instead. In this case you have to explicitly pass through the behavior of your generator (which is a method returning values like :invoke, :revoke and possibly others, depending on which command -- rails generate, rails destroy, rails update, etc. -- called your generator) to achieve the same result as above. If you don't do this, the generator you call with Rails::Generators.invoke will also be executed when running your generator with rails destroy.
Alternatively you could stick to invoke and try to tamper with Thors invocation system. See also here for example.
Generators are based off of Thor, so you can use the apply method.
This is what the Rails Templater gem does. (Here's a walk through the Rails Templater gem.)
Take a look at the scaffold generator that comes with rails.
/Users/XYZ/sources/rails/railties/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb
def manifest
record do |m|
#....rest of the source is removed for brevity....
m.dependency 'model', [name] + #args, :collision => :skip
end
end
Here the scaffold generator is using the model generator. So take a look at the dependency method. You can find the API docs for it over here.

rails script/generate skip unnecessary files by default

Script/generate became very annoying since I started using rspec etc. I dont need unit test files and fixtures anymore, but script/generate makes them anyway.
Is it possible to set --skip-fixtures and --skip-test to be default system-wide (or at least project-wide)?
You can edit your applications script/generate file to auto append options
#!/usr/bin/env ruby
ARGV << "--skip-fixture" if ["model"].include?(ARGV[0])
require File.dirname(__FILE__) + '/../config/boot'
require 'commands/generate'
Well, for starters,
ruby script/generate rspec_model
ruby script/generate rspec_controller
At least that doesn't generate unit tests and it gets the specs there for me :)
But --skip-fixtures still has to get passed. I've just made my own aliases in .bash_profile
alias model='ruby script/generate rspec_model $1 --skip-fixture'
Then I can just do
model bar name:string active:boolean
and it all works :)
I use minitest_rails as my testing framework, and you can set some defaults via the config/application.rb file.
config.generators do |g|
g.test_framework :mini_test, :spec => true, :fixture => false
end
When you generate a model (and controller), it will now automatically skip the fixture. This example will also create the Unit Test using the minitest_spec format.

Resources