Coder Social home page Coder Social logo

casecommons / with_model Goto Github PK

View Code? Open in Web Editor NEW
159.0 12.0 18.0 272 KB

Dynamically build an Active Record model (with table) within a test context

Home Page: http://www.casebook.net

License: MIT License

Ruby 100.00%
activerecord testing rspec minitest ruby

with_model's Issues

Compatibility with Active Record 5

Travis CI is reporting some problems when running against the latest Rails master (which will eventually become Active Record 5)

Here are the failures: https://travis-ci.org/Casecommons/with_model/jobs/43477610

I did a git bisect on rails and found that the first commit that causes the problems is rails/rails@a975407

Obviously Rails master is nowhere near stable, so this isn't a pressing issue, but I do want to track my progress at getting to the bottom of this. We may need to change how with_model does some things, or perhaps even report an issue back up to the Rails team.

README needed

Right now we are pointing people to the spec to see example usage. But as the spec gets more and more complicated it no longer emphasizes the common use cases. We should write up a quick README.

ActiveRecord model with has_secure_password causes errors

Great library, very useful! I did however run into one problem.

While trying to test an ActiveRecord model with has_secure_password:
https://github.com/rails/rails/blob/master/activemodel/lib/active_model/secure_password.rb

I am getting the following error:

Failures:

  1. ActiveRecord behaviors with has_secure_password should be able to be authenticated
    Failure/Error: user = User.create!(:email => '[email protected]', :password => 'foobar', :password_confirmation => 'foobar')
    ActiveRecord::StatementInvalid:
    NoMethodError: undefined method `error' for nil:NilClass: INSERT INTO "with_model_users_27596" ("email", "password_digest") VALUES (?, ?)

I created a failing spec here:

https://github.com/namick/with_model/commit/861a1a4eef042db411f26a95aea46742d525dc77

I am not sure if it is something I am doing wrong or just an edge case... If you can point me in the right direction as to why this might be happening, im happy to fix it and give you a pull request on it.

Cheers

Callbacks?

I'm trying to write a dummy model to test that callbacks are used properly, but with_model throws an error:

NoMethodError:
  undefined method `_run_before_save_callbacks' for #<Dummy id: nil, created_at: nil, updated_at: nil>
# /home/fletch/.rvm/gems/ruby-2.1.7/gems/activemodel-4.2.4/lib/active_model/attribute_methods.rb:433:in `method_missing'
# /home/fletch/.rvm/gems/ruby-2.1.7/gems/activesupport-4.2.4/lib/active_support/callbacks.rb:81:in `run_callbacks'

I don't know if it's digging too deep into re-implementing the underlying details of ActiveRecord, but it would be nice to be able to use with_model to test callbacks as well.

Call of cleanup_descendants_tracking cleanup too much models

Hello,

Regarding the following part of the code:

ActiveSupport::DescendantsTracker.clear([ActiveRecord::Base])

This call has a side effect on our project: all the models that inherit ActiveRecord::Base are not available in the ActiveRecord::Base.descendants list after the upgrade to Rails 7.

I temporary handle the side effect by overriding the following line:
ActiveSupport::DescendantsTracker.clear([ActiveRecord::Base])
ActiveSupport::DescendantsTracker.clear([@model])

Is there a reason to clear all models related to ActiveRecord::Base and not only the current model?

Regards

Non-incrementing IDs cause problems with unique indexes

The error: ActiveRecord::RecordNotUnique: Mysql2::Error: Duplicate entry 'FakeRecord-1' for key 'index_entities_on_entityable'


I have a table with a polymorphic unique relationship:

create_table :entities do |t|
  t.references :entityable, null: false, polymorphic: true, index: { unique: true }
end

I've implemented the logic for this relationship in a Concern (called Entityable). This involves creating an associated Entity automatically on creation of a model which includes Entityable:

before_validation -> { Entity.create!(entityable: self) if entity.nil? }, on: :create

I'm using with_model to test this Concern outside of an existing model:

class EntityableTest < ActiveSupport::TestCase
  with_model :FakeRecord do
    table do |t|
      t.string :name
    end

    model do
      include Concerns::Entityable
    end
  end

  def test_something
    record = FakeRecord.create!(name: 'test')
    # assert that this creates an Entity correctly...
  end
end

Here, the test passes on the first run, but on subsequent runs, calling FakeRecord.create! gives the error ActiveRecord::RecordNotUnique: Mysql2::Error: Duplicate entry 'FakeRecord-1' for key 'index_entities_on_entityable'. It seems that what's happening is a FakeRecord with ID of 1 is trying to be created, and an index entry already exists for that.

This is strange to me for 2 reasons:

  1. I know that when creating regular ActiveRecord models, the IDs are incremented even between test runs, so this issue doesn't occur.
  2. I would think that indexes are cleared out between runs similar to how tables are, but maybe this is not the case.

Regardless, it would seem that the fix for this would be incrementing model IDs rather than restarting from 1 each time. In the meantime, it seems impossible to effectively test unique indexes via with_model.

Please let me if there is any need for clarification. Thanks!

Feature request: setup model once per per context, not per example

At the moment, it seems to create table on every example (unless I am missing something).

It would be nice to have an option to do it once per with_table declaration, purely to make it faster.


Otherwise, thank you very much for a great gem. I also quite like how it adds the documentation aspect to the spec. Like here for instance:

describe DomainNameValidator do
  with_model :site do
    table do |t|
      t.string :host_name
    end

    model do
      validates :host_name, domain_name: true
    end
  end
  ...

with_model block shows what the table should have in order to use this custom validation and how to use it. Me like!

Jenkins Issue

Hello,

Using jenkins on our setup we get the following errors (rake spec)

An error occurred in an after hook
  NameError: constant Object::Voucher not defined
  occurred at /var/lib/jenkins/jobs/project/workspace/vendor/bundle/ruby/2.1.0/gems/with_model-1.1.0/lib/with_model/constant_stubber.rb:18:in `remove_const'

F
An error occurred in an after hook
  NameError: constant Object::Voucher not defined
  occurred at /var/lib/jenkins/jobs/project/workspace/vendor/bundle/ruby/2.1.0/gems/with_model-1.1.0/lib/with_model/constant_stubber.rb:18:in `remove_const'

F
An error occurred in an after hook
  NameError: constant Object::Voucher not defined
  occurred at /var/lib/jenkins/jobs/project/workspace/vendor/bundle/ruby/2.1.0/gems/with_model-1.1.0/lib/with_model/constant_stubber.rb:18:in `remove_const'

The Voucher is with_model object:

with_model :voucher do
    table do |t|
      t.string :ticket
    end

    model do
      attr_accessor :validation_hash
      validates :ticket, ticket: true
    end
  end

Any idea/solution? Thanks

Specs don't run in Ruby 3.1.0 - NoMethodError: super: no superclass method `descendants' for ActiveRecord::Base:Class

❯ ruby -v
ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [arm64-darwin21]
❯ rspec --backtrace

An error occurred while loading ./spec/active_record_behaviors_spec.rb.
Failure/Error: ActiveRecord::Base.establish_connection(adapter: adapter, database: ':memory:')

NoMethodError:
  super: no superclass method `descendants' for ActiveRecord::Base:Class
# /Users/grant/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/activerecord-7.0.0/lib/active_record/dynamic_matchers.rb:22:in `method_missing'
# /Users/grant/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/activesupport-7.0.0/lib/active_support/descendants_tracker.rb:90:in `descendants'
# /Users/grant/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/activesupport-7.0.0/lib/active_support/callbacks.rb:923:in `block in define_callbacks'
# /Users/grant/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/activesupport-7.0.0/lib/active_support/callbacks.rb:920:in `each'
# /Users/grant/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/activesupport-7.0.0/lib/active_support/callbacks.rb:920:in `define_callbacks'
# /Users/grant/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/activemodel-7.0.0/lib/active_model/validations.rb:50:in `block in <module:Validations>'
# /Users/grant/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/activesupport-7.0.0/lib/active_support/concern.rb:136:in `class_eval'
# /Users/grant/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/activesupport-7.0.0/lib/active_support/concern.rb:136:in `append_features'
# /Users/grant/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/activesupport-7.0.0/lib/active_support/concern.rb:133:in `include'
# /Users/grant/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/activesupport-7.0.0/lib/active_support/concern.rb:133:in `block in append_features'
# /Users/grant/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/activesupport-7.0.0/lib/active_support/concern.rb:133:in `each'
# /Users/grant/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/activesupport-7.0.0/lib/active_support/concern.rb:133:in `append_features'
# /Users/grant/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/activerecord-7.0.0/lib/active_record/base.rb:309:in `include'
# /Users/grant/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/activerecord-7.0.0/lib/active_record/base.rb:309:in `<class:Base>'
# /Users/grant/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/activerecord-7.0.0/lib/active_record/base.rb:282:in `<module:ActiveRecord>'
# /Users/grant/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/activerecord-7.0.0/lib/active_record/base.rb:15:in `<top (required)>'
# ./spec/spec_helper.rb:29:in `require'
# ./spec/spec_helper.rb:29:in `<top (required)>'
# <internal:/Users/grant/.rbenv/versions/3.1.0/lib/ruby/3.1.0/rubygems/core_ext/kernel_require.rb>:85:in `require'
# <internal:/Users/grant/.rbenv/versions/3.1.0/lib/ruby/3.1.0/rubygems/core_ext/kernel_require.rb>:85:in `require'
# ./spec/active_record_behaviors_spec.rb:3:in `<top (required)>'
# /Users/grant/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/rspec-core-3.10.1/lib/rspec/core/configuration.rb:2112:in `load'
# /Users/grant/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/rspec-core-3.10.1/lib/rspec/core/configuration.rb:2112:in `load_file_handling_errors'
# /Users/grant/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/rspec-core-3.10.1/lib/rspec/core/configuration.rb:1615:in `block in load_spec_files'
# /Users/grant/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/rspec-core-3.10.1/lib/rspec/core/configuration.rb:1613:in `each'
# /Users/grant/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/rspec-core-3.10.1/lib/rspec/core/configuration.rb:1613:in `load_spec_files'
# /Users/grant/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/rspec-core-3.10.1/lib/rspec/core/runner.rb:102:in `setup'
# /Users/grant/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/rspec-core-3.10.1/lib/rspec/core/runner.rb:86:in `run'
# /Users/grant/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/rspec-core-3.10.1/lib/rspec/core/runner.rb:71:in `run'
# /Users/grant/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/rspec-core-3.10.1/lib/rspec/core/runner.rb:45:in `invoke'
# /Users/grant/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/rspec-core-3.10.1/exe/rspec:4:in `<top (required)>'
# /Users/grant/.rbenv/versions/3.1.0/bin/rspec:25:in `load'
# /Users/grant/.rbenv/versions/3.1.0/bin/rspec:25:in `<main>'

Descendants tracking error when `cache_classes = true`

We're getting an error when we have cache_classes = true on CI with Rails 7.0.1:

 RuntimeError:
              DescendantsTracker.clear was disabled because config.cache_classes = true
            # /home/circleci/app/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.1/lib/active_support/descendants_tracker.rb:119:in `clear'
            # /home/circleci/app/vendor/bundle/ruby/2.7.0/gems/with_model-2.1.6/lib/with_model/model.rb:59:in `cleanup_descendants_tracking'
            # /home/circleci/app/vendor/bundle/ruby/2.7.0/gems/with_model-2.1.6/lib/with_model/model.rb:38:in `destroy'
            # /home/circleci/app/vendor/bundle/ruby/2.7.0/gems/with_model-2.1.6/lib/with_model.rb:53:in `block in setup_object'

Record creation fails when one with_model has a foreign key pointing to another with_model

I made you a lil repro script. The child without foreign key is created just fine, the child with foreign key hits an exception.

require "bundler/inline"

gemfile do
  source "https://rubygems.org"
  gem "sqlite3"
  gem "combustion"
  gem "with_model"
  gem "minitest"
end

require "combustion"
require "minitest/autorun"
require "with_model"

# A hack to override some Combustion behavior
module Bundler
  def self.require(*)
    # noop
  end
end
ENV['DATABASE_URL'] = "sqlite3::memory:"
ENV['RAILS_ENV'] = "test"
Combustion.initialize! :active_record, database_reset: false, load_schema: false do
  config.enable_reloading = true
end

WithModel.runner = :minitest

class ForeignKeysSpec < Minitest::Spec
  extend WithModel

  with_model :Parent do
    model do
      has_many :children
      has_many :children_with_foreign_key, class_name: "ChildWithForeignKey"
    end
  end

  with_model :Child do
    table do |t|
      t.references :parent
    end

    model do
      belongs_to :parent
    end
  end

  with_model :ChildWithForeignKey do
    table do |t|
      t.references :parent, foreign_key: true
    end

    model do
      belongs_to :parent
    end
  end

  it "creates a child without a foreign key" do
    parent = Parent.create!
    child = parent.children.create!
    assert_equal parent, child.parent
  end

  it "creates a child with a foreign key" do
    parent = Parent.create!
    assert_raises ActiveRecord::StatementInvalid do
      parent.children_with_foreign_key.create!
    end
  end
end

Rails 4.2.1 NameError: constant not defined

After upgrading to Rails 4.2.1 we are experiencing the following error:

An error occurred in an after hook
    NameError: constant Object::Presenter not defined
    occurred at /Users/foo/.rvm/gems/ruby-2.1.5@foo-rails4/gems/with_model-1.2.1/lib/with_model/constant_stubber.rb:18:in `remove_const'

I haven't had a chance to look into the root cause of this issue but wanted to bring it to your attention right away.

Generated index names on midsize model names are excessively large under AR5+

We've been migrating a project that uses with_mocks in its specs to Rails 5 from 4.2, and are finding that the autogenerated names for indices are now frequently longer than the character limit for our underlying database (PG9.6). This may not be a bug in with_mocks per se, if it's just a result of changes upstream in ActiveRecord, but documenting this behavior would be helpful for future users.

Manually specifying a shorter index name addresses the issue for now.

Breaking example:

require "rails_helper"

describe "index issue" do
  with_model :MidsizeNameChild do
    table do |t|
      t.belongs_to :midsize_name_parent
    end

    model do
      belongs_to :midsize_name_parent
    end
  end

  with_model :MidsizeNameParent do
    model do
      has_many :midsize_name_children
    end
  end

  let(:instance) { MidsizeNameParent.create }

  describe "try to instantiate" do
    it "is an example" do
      instance
    end
  end
end

Produces: Index name 'index_with_model_midsize_name_children_39923_70337190696860_on_midsize_name_parent_id' on table 'with_model_midsize_name_children_39923_70337190696860' is too long; the limit is 63 characters (though UUID will vary)

Working example:

require "rails_helper"

describe "index issue" do
  with_model :MidsizeNameChild do
    table do |t|
      t.belongs_to :midsize_name_parent,
                   index: { name: "shorter_index_name" }
    end

    model do
      belongs_to :midsize_name_parent
    end
  end

  with_model :MidsizeNameParent do
    model do
      has_many :midsize_name_children
    end
  end

  let(:instance) { MidsizeNameParent.create }

  describe "try to instantiate" do
    it "is an example" do
      instance
    end
  end
end

(passes)

duplicate model names in sibling contexts pollute each other when one is referenced before defined

The problem here is that posts is referenced in the second context before it is defined so the Post class that is referenced is the one from the previous context. You can verify this by looking at the object ids.

  context "when the model has a circular nested attribute reference" do
    with_model :blog do
      table {}
      model do
        has_many :posts
        accepts_nested_attributes_for :posts
      end
    end

    with_model :post do
      table do |t|
        t.integer :blog_id
      end

      model do
        puts self.object_id
        belongs_to :blog
        accepts_nested_attributes_for :blog
      end
    end

    subject { Post.to_xsd }

    it "should generate a valid XSD" do
      validate_xsd(subject)
    end

  end

  context "when the model has a nested reference that references another nested reference" do
    with_model :blog do
      table {}
      model do
        has_many :posts
        has_many :readers
        accepts_nested_attributes_for :posts
        accepts_nested_attributes_for :readers
      end
    end

    with_model :post do
      table do |t|
        t.integer :blog_id
      end

      model do
        puts self.object_id
        belongs_to :blog
        has_many :readers
        accepts_nested_attributes_for :blog
        accepts_nested_attributes_for :readers
      end
    end

    with_model :reader do
      table do |t|
        t.integer :blog_id
        t.integer :post_id
      end
    end

    subject { Post.to_xsd }

    it "should generate a valid XSD" do
      validate_xsd(subject)
    end

  end
end

Maybe, release?

Hey there!

Any plans on releasing a new version? The latest release is not compatible with Rails 6 and master is #28.

Rails 6.1 support

Currently, the gem has a dependency on activerecord < 6.1. With the release of rails 6.1.0.rc1, it'd be good to make a new release of with_model that allows activerecord 6.1 :)

undefined method `stub_const'

I get the following error while running guard and RSpec 2.12.2

Failure/Error: Unable to find matching line from backtrace
     NoMethodError:
       undefined method `stub_const' for #<RSpec::Core::ExampleGroup::Nested_1:0x007fd11c8f7498>

Any idea?

Minitest support

There seem to be a couple of sticky points to full minitest support. Namely:

a) The Minitest setup block executes before with_model so the class is not accessible. In Minitest, this block is similar to before(:all) in RSpec so used widely in initializing the test scenario.
b) There's a hard-coded call to before in the with_model block, which isn't supported by Minitest. It's possible to get around it with a extend Minitest::Spec::DSL but that seems a bit unfortunate.

Any directions on how best to resolve? I'm happy to submit a PR, just looking for guidance and perhaps willingness to persevere on a solution.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.