Last active
December 1, 2023 11:20
-
-
Save fjfish/78bd55ffc708c16a400d to your computer and use it in GitHub Desktop.
Automatic generation of rspec model tests and factories for factory girl
This file contains hidden or 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
class Models | |
def self.generate what = :model | |
Rails.application.eager_load! | |
ActiveRecord::Base.descendants.each do |model| | |
method(what)[model] | |
end | |
true | |
end | |
def self.factory model | |
factory_file_name = "spec/factories/#{model.name.underscore}.rb" | |
unless File.exists?(factory_file_name) | |
File.open(factory_file_name,"w") do |file| | |
factory_for model, file | |
end | |
end | |
end | |
def self.factory_for model, file | |
file << <<-EOT | |
FactoryBot.define do | |
factory :#{model.name.underscore}, :class => '#{model.name}' do | |
#{factory_cols model} | |
end | |
end | |
EOT | |
end | |
def self.factory_cols model | |
associations = model.reflections | |
"".tap do |output_text| | |
model.columns.each do |col| | |
next if col.name == 'id' | |
stripped_name = col.name.gsub(/_id$/,'').to_sym | |
output_text << "\n " | |
assoc = associations[stripped_name] | |
if assoc && [:has_one,:belongs_to].include?(assoc.macro) | |
output_text << if assoc.options[:class_name] | |
"association :#{stripped_name.to_s}, factory: :#{assoc.options[:class_name].underscore}" | |
else | |
stripped_name.to_s | |
end | |
else | |
output_text << "#{preprocess_name col.name, col.type } #{factory_default_for(col.name, col.type)}" | |
end | |
end | |
end | |
end | |
def self.preprocess_name name, type | |
case name | |
when /retry/ | |
"self.#{name}" | |
when /e.*mail/,/name/ | |
if [:text,:string].include?(type) | |
"sequence(:#{name})" | |
else | |
name | |
end | |
else | |
name | |
end | |
end | |
def self.factory_default_for name, type | |
case type | |
when :integer, :decimal | |
"1" | |
when :date | |
"Time.now.to_date" | |
when :datetime | |
"Time.now" | |
when :boolean | |
"true" | |
when :spatial | |
"nil" | |
else | |
case name | |
when /e.*mail/ | |
'{ |n| "test#{n}@example.com" }' | |
when /name/ | |
'{ |n| "name#{n}" }' | |
when /country/ | |
'"GB"' | |
when /_ip$/ | |
'"192.168.0.1"' | |
when /phone/ | |
'"+44000000000"' | |
else | |
'"test123"' | |
end | |
end | |
end | |
def self.model model | |
test_file_name = "spec/models/#{model.name.underscore}_spec.rb" | |
unless File.exists?(test_file_name) | |
File.open(test_file_name,"w") do |file| | |
describe_model model, file | |
end | |
end | |
end | |
def self.describe_model model, file | |
file << <<-EOT | |
require 'rails_helper' | |
describe #{model.name}, :type => :model do | |
# let (:subject) { build :#{model.name.underscore} } | |
#{read_write_tests model} | |
#{associations model.reflections} | |
end | |
EOT | |
end | |
def self.read_write_tests model | |
" context \"validation\" do".tap do |output_text| | |
model.validators.select { |val| val.is_a? ActiveModel::Validations::PresenceValidator }.map(&:attributes). | |
flatten.each { |col| output_text << "\n it { should validate_presence_of :#{col} }"} | |
model.validators.select { |val| !val.is_a? ActiveModel::Validations::PresenceValidator }. | |
each { |col| output_text << "\n it \"#{col.class.to_s.demodulize.underscore} test for #{col.attributes.map(&:to_sym).to_s}\""} | |
end << "\n end" | |
end | |
def self.associations reflections | |
" context \"associations\" do".tap do |output_text| | |
reflections.each_pair { |key,assoc| output_text << "\n it { should #{translate_assoc assoc.macro} :#{test_assoc_name assoc} }"} | |
end << "\n end" | |
end | |
def self.translate_assoc macro | |
macro.to_s.gsub(/belongs/,'belong').gsub(/has/,'have') | |
end | |
def self.test_assoc_name assoc | |
case assoc.macro | |
when /have_many/ | |
assoc.plural_name | |
when /has_one/,/belongs_to/ | |
assoc.name | |
else | |
assoc.name | |
end | |
end | |
end |
@jmscholen - thanks - amended.
Hope you found it useful.
IIRC my local copy had to put brackets around the factory stuff, but I haven't needed it for a few days so not checked.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@fjfish small error: