-
-
Notifications
You must be signed in to change notification settings - Fork 529
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce
suspenders:factories
generator (#1136)
Maintains functionally with the [existing generator][] while adding support for the [default Rails test suite]. With this change, the generator can be invoked on a Rails application that uses RSpec or the [default Rails test suite][]. Adds generator which adds a test to lint all Factories in an effort to improve developer experience. Additionally, we remove the generation of the `dev:prime` task as we felt that should be the responsibly of another generator. [existing generator]: https://github.com/thoughtbot/suspenders/blob/main/lib/suspenders/generators/factories_generator.rb [default Rails test suite]: https://guides.rubyonrails.org/testing.html
- Loading branch information
1 parent
6b21861
commit 9a1fc45
Showing
9 changed files
with
354 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
module Suspenders | ||
module Generators | ||
class FactoriesGenerator < Rails::Generators::Base | ||
include Suspenders::Generators::Helpers | ||
|
||
source_root File.expand_path("../../templates/factories", __FILE__) | ||
desc <<~TEXT | ||
Build test data with clarity and ease. | ||
This uses FactoryBot to help you define dummy and test data for your test | ||
suite. The `create`, `build`, and `build_stubbed` class methods are directly | ||
available to all tests. | ||
We recommend putting FactoryBot definitions in one `spec/factories.rb` (or | ||
`test/factories`) file, at least until it grows unwieldy. This helps reduce | ||
confusion around circular dependencies and makes it easy to jump between | ||
definitions. | ||
Supports the default test suite and RSpec. | ||
TEXT | ||
|
||
def add_factory_bot | ||
gem_group :development, :test do | ||
gem "factory_bot_rails" | ||
end | ||
|
||
Bundler.with_unbundled_env { run "bundle install" } | ||
end | ||
|
||
def set_up_factory_bot | ||
if default_test_helper_present? | ||
insert_into_file Rails.root.join("test/test_helper.rb"), after: "class TestCase" do | ||
"\n include FactoryBot::Syntax::Methods" | ||
end | ||
elsif rspec_test_helper_present? | ||
copy_file "factory_bot_rspec.rb", "spec/support/factory_bot.rb" | ||
insert_into_file Rails.root.join("spec/rails_helper.rb") do | ||
%(Dir[Rails.root.join("spec/support/**/*.rb")].sort.each { |file| require file }) | ||
end | ||
end | ||
end | ||
|
||
def generate_empty_factories_file | ||
if default_test_suite? | ||
copy_file "factories.rb", "test/factories.rb" | ||
elsif rspec_test_suite? | ||
copy_file "factories.rb", "spec/factories.rb" | ||
end | ||
end | ||
|
||
def remove_fixture_definitions | ||
if default_test_helper_present? | ||
comment_lines "test/test_helper.rb", /fixtures :all/ | ||
end | ||
end | ||
|
||
def create_linting_test | ||
if default_test_suite? | ||
copy_file "factories_test.rb", "test/factory_bots/factories_test.rb" | ||
elsif rspec_test_suite? | ||
copy_file "factories_spec.rb", "spec/factory_bots/factories_spec.rb" | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
FactoryBot.define do | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
require "rails_helper" | ||
|
||
RSpec.describe "Factories" do | ||
it "has valid factoties" do | ||
FactoryBot.lint traits: true | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
require "test_helper" | ||
|
||
class FactoryBotsTest < ActiveSupport::TestCase | ||
class FactoryLintingTest < FactoryBotsTest | ||
test "linting of factories" do | ||
FactoryBot.lint traits: true | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
FactoryBot.use_parent_strategy = true | ||
|
||
RSpec.configure do |config| | ||
config.include FactoryBot::Syntax::Methods | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,225 @@ | ||
require "test_helper" | ||
require "generators/suspenders/factories_generator" | ||
|
||
module Suspenders | ||
module Generators | ||
class FactoriesGenerator::DefaultTest < Rails::Generators::TestCase | ||
include Suspenders::TestHelpers | ||
|
||
tests Suspenders::Generators::FactoriesGenerator | ||
destination Rails.root | ||
setup :prepare_destination | ||
teardown :restore_destination | ||
|
||
test "generator has a description" do | ||
description = <<~TEXT | ||
Build test data with clarity and ease. | ||
This uses FactoryBot to help you define dummy and test data for your test | ||
suite. The `create`, `build`, and `build_stubbed` class methods are directly | ||
available to all tests. | ||
We recommend putting FactoryBot definitions in one `spec/factories.rb` (or | ||
`test/factories`) file, at least until it grows unwieldy. This helps reduce | ||
confusion around circular dependencies and makes it easy to jump between | ||
definitions. | ||
Supports the default test suite and RSpec. | ||
TEXT | ||
|
||
assert_equal description, FactoriesGenerator.desc | ||
end | ||
|
||
test "installs gem with Bundler" do | ||
Bundler.stubs(:with_unbundled_env).yields | ||
generator.expects(:run).with("bundle install").once | ||
|
||
capture(:stdout) do | ||
generator.add_factory_bot | ||
end | ||
end | ||
|
||
test "removes fixture definitions" do | ||
File.open(app_root("test/test_helper.rb"), "w") { _1.write test_helper } | ||
|
||
run_generator | ||
|
||
assert_file app_root("test/test_helper.rb") do |file| | ||
assert_match(/# fixtures :all/, file) | ||
end | ||
end | ||
|
||
test "adds gem to Gemfile" do | ||
run_generator | ||
|
||
assert_file app_root("Gemfile") do |file| | ||
assert_match(/group :development, :test do\n gem "factory_bot_rails"\nend/, file) | ||
end | ||
end | ||
|
||
test "includes syntax methods" do | ||
File.open(app_root("test/test_helper.rb"), "w") { _1.write test_helper } | ||
|
||
run_generator | ||
|
||
assert_file app_root("test/test_helper.rb") do |file| | ||
assert_match(/class TestCase\n include FactoryBot::Syntax::Methods/, file) | ||
end | ||
end | ||
|
||
test "creates definition file" do | ||
definition_file = <<~RUBY | ||
FactoryBot.define do | ||
end | ||
RUBY | ||
|
||
run_generator | ||
|
||
assert_file app_root("test/factories.rb") do |file| | ||
assert_match definition_file, file | ||
end | ||
end | ||
|
||
test "creates linting test" do | ||
factories_test = <<~RUBY | ||
require "test_helper" | ||
class FactoryBotsTest < ActiveSupport::TestCase | ||
class FactoryLintingTest < FactoryBotsTest | ||
test "linting of factories" do | ||
FactoryBot.lint traits: true | ||
end | ||
end | ||
end | ||
RUBY | ||
|
||
run_generator | ||
|
||
assert_file app_root("test/factory_bots/factories_test.rb") do |file| | ||
assert_match factories_test, file | ||
end | ||
end | ||
|
||
private | ||
|
||
def prepare_destination | ||
mkdir "test" | ||
touch "Gemfile" | ||
end | ||
|
||
def restore_destination | ||
remove_dir_if_exists "test" | ||
remove_file_if_exists "Gemfile" | ||
remove_dir_if_exists "lib/tasks" | ||
end | ||
|
||
def test_helper | ||
<<~RUBY | ||
ENV["RAILS_ENV"] ||= "test" | ||
require_relative "../config/environment" | ||
require "rails/test_help" | ||
module ActiveSupport | ||
class TestCase | ||
# Run tests in parallel with specified workers | ||
parallelize(workers: :number_of_processors) | ||
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. | ||
fixtures :all | ||
# Add more helper methods to be used by all tests here... | ||
end | ||
end | ||
RUBY | ||
end | ||
end | ||
|
||
class FactoriesGenerator::RSpecTest < Rails::Generators::TestCase | ||
include Suspenders::TestHelpers | ||
|
||
tests Suspenders::Generators::FactoriesGenerator | ||
destination Rails.root | ||
setup :prepare_destination | ||
teardown :restore_destination | ||
|
||
test "includes syntax methods" do | ||
touch("spec/rails_helper.rb") | ||
factory_bot_config = <<~RUBY | ||
FactoryBot.use_parent_strategy = true | ||
RSpec.configure do |config| | ||
config.include FactoryBot::Syntax::Methods | ||
end | ||
RUBY | ||
|
||
run_generator | ||
|
||
assert_file app_root("spec/support/factory_bot.rb") do |file| | ||
assert_match factory_bot_config, file | ||
end | ||
assert_file app_root("spec/rails_helper.rb") do |file| | ||
assert_match(/Dir\[Rails\.root\.join\("spec\/support\/\*\*\/\*\.rb"\)\]\.sort\.each { \|file\| require file }/, file) | ||
end | ||
end | ||
|
||
test "creates definition file" do | ||
definition_file = <<~RUBY | ||
FactoryBot.define do | ||
end | ||
RUBY | ||
|
||
run_generator | ||
|
||
assert_file app_root("spec/factories.rb") do |file| | ||
assert_match definition_file, file | ||
end | ||
end | ||
|
||
test "does not modify rails_helper if it's configured to include support files" do | ||
touch("spec/rails_helper.rb") | ||
rails_helper = <<~RUBY | ||
Dir[Rails.root.join("spec/support/**/*.rb")].sort.each { |file| require file } | ||
RUBY | ||
File.open(app_root("spec/rails_helper.rb"), "w") { _1.write rails_helper } | ||
|
||
run_generator | ||
|
||
assert_file app_root("spec/rails_helper.rb") do |file| | ||
assert_equal rails_helper, file | ||
end | ||
end | ||
|
||
test "creates linting test" do | ||
factories_spec = <<~RUBY | ||
require "rails_helper" | ||
RSpec.describe "Factories" do | ||
it "has valid factoties" do | ||
FactoryBot.lint traits: true | ||
end | ||
end | ||
RUBY | ||
|
||
run_generator | ||
|
||
assert_file app_root("spec/factory_bots/factories_spec.rb") do |file| | ||
assert_match factories_spec, file | ||
end | ||
end | ||
|
||
private | ||
|
||
def prepare_destination | ||
mkdir "spec" | ||
touch "spec/spec_helper.rb" | ||
touch "Gemfile" | ||
end | ||
|
||
def restore_destination | ||
remove_dir_if_exists "spec" | ||
remove_file_if_exists "Gemfile" | ||
remove_dir_if_exists "lib/tasks" | ||
end | ||
end | ||
end | ||
end |