1
0
Fork 0
mirror of https://github.com/thoughtbot/shoulda-matchers.git synced 2022-11-09 12:01:38 -05:00
thoughtbot--shoulda-matchers/spec/support/unit/helpers/model_builder.rb
Anthony Navarre + Elliot Winkler eaaa2d83e5 allow_value: Raise error if attr sets value differently
`allow_value` will now raise a CouldNotSetAttribute error if the
attribute in question cannot be changed from a non-nil value to a nil
value, or vice versa. In other words, these are the exact cases in which
the error will occur:

* If you're testing whether the attribute allows `nil`, but the
  attribute detects and ignores nil. (For instance, you have a model
  that `has_secure_password`. This will add a #password= method to your
  model that is defined in a such a way that you cannot clear the
  password by setting it to nil -- nothing happens.)
* If you're testing whether the attribute allows a non-nil value, but
  the attribute fails to set that value. (For instance, you have an
  ActiveRecord model. If ActiveRecord cannot typecast the value in the
  context of the column, then it will do nothing, and the attribute will be
  effectively set to nil.)

What's the reasoning behind this change? Simply put, if you are assuming
that the attribute is changing but in fact it is not, then the test
you're writing isn't the test that actually gets run. We feel that this
is dishonest and produces an invalid test.
2015-02-17 23:09:56 -07:00

134 lines
3.3 KiB
Ruby

require_relative 'class_builder'
module UnitTests
module ModelBuilder
include ClassBuilder
def self.configure_example_group(example_group)
example_group.include(self)
example_group.after do
clear_column_caches
drop_created_tables
end
end
def create_table(table_name, options = {}, &block)
connection = ActiveRecord::Base.connection
begin
connection.execute("DROP TABLE IF EXISTS #{table_name}")
connection.create_table(table_name, options, &block)
created_tables << table_name
connection
rescue Exception => e
connection.execute("DROP TABLE IF EXISTS #{table_name}")
raise e
end
end
def define_model_class(class_name, &block)
define_class(class_name, ActiveRecord::Base, &block)
end
def define_active_model_class(class_name, options = {}, &block)
accessors = options.fetch(:accessors, [])
define_class(class_name) do
include ActiveModel::Validations
def initialize(attributes = {})
attributes.each do |name, value|
__send__("#{name}=", value)
end
end
accessors.each do |column|
attr_accessor column.to_sym
end
if block_given?
class_eval(&block)
end
end
end
def define_model(name, columns = {}, &block)
class_name = name.to_s.pluralize.classify
table_name = class_name.tableize.gsub('/', '_')
table_block = lambda do |table|
columns.each do |column_name, specification|
if specification.is_a?(Hash)
column_type = specification[:type]
column_options = specification.fetch(:options, {})
else
column_type = specification
column_options = {}
end
begin
table.__send__(column_type, column_name, column_options)
rescue NoMethodError
raise NoMethodError, "#{Tests::Database.instance.adapter_class} does not support :#{column_type} columns"
end
end
end
if columns.key?(:id) && columns[:id] == false
columns.delete(:id)
create_table(table_name, id: false, &table_block)
else
create_table(table_name, &table_block)
end
model = define_model_class(class_name).tap do |m|
if block
if block.arity == 0
m.class_eval(&block)
else
block.call(m)
end
end
m.table_name = table_name
end
defined_models << model
model
end
private
def clear_column_caches
# Rails 4.x
if ActiveRecord::Base.connection.respond_to?(:schema_cache)
ActiveRecord::Base.connection.schema_cache.clear!
# Rails 3.1 - 4.0
elsif ActiveRecord::Base.connection_pool.respond_to?(:clear_cache!)
ActiveRecord::Base.connection_pool.clear_cache!
end
defined_models.each do |model|
model.reset_column_information
end
end
def drop_created_tables
connection = ActiveRecord::Base.connection
created_tables.each do |table_name|
connection.execute("DROP TABLE IF EXISTS #{table_name}")
end
end
def created_tables
@_created_tables ||= []
end
def defined_models
@_defined_models ||= []
end
end
end