Giter Site home page Giter Site logo

zeisler / active_mocker Goto Github PK

View Code? Open in Web Editor NEW
505.0 10.0 29.0 1.79 MB

Generate mocks from ActiveRecord models for unit tests that run fast because they don’t need to load Rails or a database.

Home Page: http://dustinzeisler.com/active_mocker/

License: MIT License

Ruby 95.28% JavaScript 0.25% CSS 0.25% HTML 4.21%

active_mocker's Introduction

ActiveMocker

Gem Version Build Status Gitter chat

Description

Creates stub classes from any ActiveRecord model.

By using stubs in your tests you don't need to load Rails or the database, sometimes resulting in a 10x speed improvement.

ActiveMocker analyzes the methods and database columns to generate a Ruby class file.

The stub file can be run standalone and comes included with many useful parts of ActiveRecord.

Stubbed out methods contain their original argument signatures or ActiveMocker's friendly code can be brought over in its entirety.

Mocks are regenerated when the schema is modified so your mocks won't go stale, preventing the case where your unit tests pass but production code fails.

Examples from a real app

	Finished in 1 seconds
	374 examples, 0 failures

Around the web

"Mocking ActiveRecord with ActiveMocker" by Envy



Documentation Inline docs

rdoc


Contact

Ask a question in the chat room.


Installation

Add this line to your application's Gemfile:

group :development, :test do
  gem 'active_mocker'
end

It needs to be in development as well as test groups, as the development environment is where mocks will be generated. Then execute:

$ bundle

Or install it yourself as:

$ gem install active_mocker

Dependencies

  • Tested with Rails 4.x, 5.x, 6.x
  • Requires Ruby MRI >= 2.4.x

Setup

See example_rails_app for complete setup.

Generate Mocks

Running this rake task builds/rebuilds the mocks. It will be ran automatically after every schema modification. If the model changes, this rake task needs to be called manually. You could add a file watcher for when your models change and have it run the rake task.

rake active_mocker:build

Usage

#db/schema.rb

ActiveRecord::Schema.define(version: 20140327205359) do

  create_table "people", force: true do |t|
    t.integer  "account_id"
    t.string   "first_name",        limit: 128
    t.string   "last_name",         limit: 128
    t.string   "address",           limit: 200
    t.string   "city",              limit: 100
  end

end

#app/models/person.rb

class Person < ActiveRecord::Base
  belongs_to :account

  def self.bar(name, type=nil)
	puts name
  end

end

Using With Rspec, --tag active_mocker:true

require 'rspec'
require 'active_mocker/rspec_helper'
require 'spec/mocks/person_mock'
require 'spec/mocks/account_mock'

describe 'Example', active_mocker:true do

  before do
	Person.create # stubbed for PersonMock.create
  end

end

  • Assigning the tag active_mocker:true will stub any ActiveRecord model Constants for Mock classes in an it or a before/after(:each). This removes any need for dependency injection. Write tests and code like you would normally.
  • To stub any Constants in before(:all), after(:all) use active_mocker.find('ClassName').
  • Mock state will be cleaned up for you in an after(:all). To clean state by yourself, use active_mocker.delete_all.

Person.column_names
  => ["id", "account_id", "first_name", "last_name", "address", "city"]

person = Person.new( first_name:  "Dustin", 
    				 last_name:   "Zeisler", 
    				 account:      Account.new )
  => "#<PersonMock id: nil, account_id: nil, first_name: "Dustin", last_name: "Zeisler", address: nil, city: nil>"

person.first_name
  => "Dustin"

When schema.rb changes, the mock fails

(After rake db:migrate is called the mocks will be regenerated.)

#db/schema.rb

ActiveRecord::Schema.define(version: 20140327205359) do

  create_table "people", force: true do |t|
    t.integer  "account_id"
    t.string   "f_name",        limit: 128
    t.string   "l_name",        limit: 128
    t.string   "address",       limit: 200
    t.string   "city",          limit: 100
  end

end

Person.new(first_name: "Dustin", last_name: "Zeisler")
  =>#<UnknownAttributeError unknown attribute: first_name >

Creating Custom collections

If you want to create a custom set of records that is not part of the global collection for model. (ie. for stubbing in a test)

User::ScopeRelation.new([User.new, User.new])

This gives the full query API (ie. find_by, where, etc).

This is not a feature available in ActiveRecord, so do not include this where you intend to swap for ActiveRecord.

Optional Features

Use theses defaults if you are starting fresh

ActiveMocker::LoadedMocks.features.enable(:timestamps)
ActiveMocker::LoadedMocks.features.enable(:delete_all_before_example)
ActiveMocker::LoadedMocks.features.enable(:stub_active_record_exceptions)

timestamps

Enables created_at and updated_at to be updated on save and create

delete_all_before_example

When using "active_mocker/rspec_helper", it deletes all records from all mocks before each example.

stub_active_record_exceptions

When requiring "active_mocker/rspec_helper", and adding active_mocker: true to the describe metadata, these errors will be auto stubbed:

  • ActiveRecord::RecordNotFound
  • ActiveRecord::RecordNotUnique
  • ActiveRecord::UnknownAttributeError

Copy over Mock safe methods into the generated mock

Adding the comment ActiveMocker.safe_methods at the top of a class marks it as safe to copy to the mock. Be careful. It should not contain anything that ActiveMocker cannot run.

# ActiveMocker.safe_methods(scopes: [], instance_methods: [:full_name], class_methods: [])
class User
  def full_name
    "#{first_name} + #{last_name}"
  end
end

Mocking Methods

Rspec 3 Mocks - verify double

Verifying doubles is a stricter alternative to normal doubles that provides guarantees about what is being verified. When using verifying doubles, RSpec will check if the methods being stubbed are actually present on the underlying object if it is available. rspec-mocks/docs/verifying-doubles

RSpec.configure do |config|
  config.mock_framework = :rspec
  config.mock_with :rspec do |mocks|
    mocks.verify_doubled_constant_names = true
    mocks.verify_partial_doubles = true
  end
end
Person.bar('baz')
  => NotImplementedError: ::bar is not Implemented for Class :PersonMock. To continue stub the method.

allow(Person).to receive(:bar) do |name, type=nil|
  "Now implemented with #{name} and #{type}"
end

Person.bar('foo', 'type')
=> "Now implemented with foo and type"

When the model changes, the mock fails

(Requires a regeneration of the mocks files.)

#app/models/person.rb

class Person < ActiveRecord::Base
  belongs_to :account

  def self.bar(name)
    puts name
  end

end

Person.bar('foo', 'type')
  => ArgumentError: wrong number of arguments (2 for 1)

#app/models/person.rb

class Person < ActiveRecord::Base
  belongs_to :account

  def self.foo(name, type=nil)
    puts name
  end

end

allow(Person).to receive(:bar) do |name, type=nil|
  "Now implemented with #{name} and #{type}"
end
=> RSpec::Mocks::MockExpectationError: PersonMock does not implement: bar

Constants and Modules

  • Any locally defined modules will not be included or extended. It can be disabled by ActiveMocker::Config.disable_modules_and_constants = true

class Person < ActiveRecord::Base
  CONSTANT_VALUE = 13
end

PersonMock::CONSTANT_VALUE
  => 13

Scoped Methods

  • Any chained scoped methods will be available when the mock file that defines it is required. When called, it raises a NotImplementedError. Stub the method with a value to continue.

Managing Mocks

require "active_mocker/rspec_helper"

active_mocker.delete_all # Delete all records from loaded mocks

active_mocker.find("User") # Find a mock by model name. Useful in before(:all)/after(:all) where automatic constant stubbing is unavailable.

active_mocker.mocks.except("User").delete_all # Delete all loaded mock expect the User mock.

ActiveRecord supported methods

See Documentation for a complete list of methods and usage.

Class Methods - docs

  • new
  • create/create!
  • column_names/attribute_names
  • delete_all/destroy_all
  • table_name
  • slice
  • alias_attributes

Query Methods - docs

  • all
  • find
  • find_by/find_by!
  • find_or_create_by
  • find_or_initialize_by
  • where(conditions_hash)
  • where(key: array_of_values)
  • where.not(conditions_hash)
  • delete_all/destroy_all
  • delete_all(conditions_hash)
  • destroy(id)/delete(id)
  • update_all
  • update(id, attributes)
  • count
  • uniq
  • first/last
  • average(:field_name)
  • minimum(:field_name)
  • maximum(:field_name)
  • sum(:field_name)
  • order(:field_name)
  • reverse_order
  • limit
  • none

Relation Methods - docs

  • concat
  • include
  • push
  • clear
  • take
  • empty?
  • replace
  • any?
  • many?

instance methods - docs

  • attributes
  • update
  • save/save!
  • write_attribute/read_attribute
  • delete
  • new_record?
  • persisted?
  • reload
  • attribute_names
  • attribute_present?
  • has_attribute?
  • slice
  • attribute_alias?
  • alias_attributes
  • touch

has_one/belongs_to/has_many

  • build_< association >
  • create_< association >
  • create_< association >!
  • < association >.create
  • < association >.build

Schema/Migration Option Support

  • A db/schema.rb is not required.
  • All schema types are supported and coerced by Virtus. If coercion fails, the passed value will be retained.
  • Default value is supported.
  • Scale and Precision are not supported.

Known Limitations

  • Namespaced modules are not currently supported.
  • When an association is set in one object it may not always be reflective in other objects, especially when it is a non standard/custom association. See test_rails_4_app/spec/active_record_compatible_api.rb for a complete list of supported associations.
  • Validation/Callbacks are not supported.
  • Sql queries, joins, etc will never be supported.
  • A record that has been created and then is modified will persist changes without calling #save. Beware of this difference.
  • This is not a full replacement for ActiveRecord.
  • Primary key will always default to id. If this is causing a problem, feel free to open an issue (or even better, a PR =)).

Inspiration

Thanks to Jeff Olfert for being my original inspiration for this project.

Contributing

Your contributions are welcome!

  1. Fork it ( http://github.com/zeisler/active_mocker/fork )
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

active_mocker's People

Contributors

cschramm avatar dandlezzz avatar fgrehm avatar markburns avatar olleolleolle avatar rrrene avatar seanhussey avatar svanzoest avatar tiagofsilva avatar zeisler avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

active_mocker's Issues

Correctly Support many through

has_many :decision_mortgages
has_many :credit_liabilities, through: :decision_mortgages

ActiveMocker treats credit_liabilities as an association on the parent model and ignores the through.

The work around is

Model.new(credit_liabilities: credit_liabilities)

It should work like:

Model.new(decision_mortgages: [DecisionMortgage.new(credit_liability: CreditLiability.create)])

active_mocker appears to break with rails 4.2.1

After upgrading to rails 4.2.1, active_mocker:build completes fine, however the generated files do not execute. I have attempted to fork, pull, investigate however my limited rails knowledge has prevented me from getting too far.

/home/krimsonkla/Candi/spec/mocks/access_token_mock.rb:175: syntax error, unexpected end-of-input, expecting keyword_end
from /home/krimsonkla/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/activesupport-4.2.1/lib/active_support/dependencies.rb:274:in block in require' from /home/krimsonkla/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/activesupport-4.2.1/lib/active_support/dependencies.rb:240:inload_dependency'
from /home/krimsonkla/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/activesupport-4.2.1/lib/active_support/dependencies.rb:274:in require' from /home/krimsonkla/Candi/spec/spec_helper.rb:25:inblock in <top (required)>'
from /home/krimsonkla/Candi/spec/spec_helper.rb:25:in each' from /home/krimsonkla/Candi/spec/spec_helper.rb:25:in<top (required)>'
from /home/krimsonkla/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/activesupport-4.2.1/lib/active_support/dependencies.rb:274:in require' from /home/krimsonkla/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/activesupport-4.2.1/lib/active_support/dependencies.rb:274:inblock in require'
from /home/krimsonkla/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/activesupport-4.2.1/lib/active_support/dependencies.rb:240:in load_dependency' from /home/krimsonkla/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/activesupport-4.2.1/lib/active_support/dependencies.rb:274:inrequire'
from /home/krimsonkla/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/rspec-core-3.2.3/lib/rspec/core/configuration.rb:1181:in block in requires=' from /home/krimsonkla/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/rspec-core-3.2.3/lib/rspec/core/configuration.rb:1181:ineach'
from /home/krimsonkla/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/rspec-core-3.2.3/lib/rspec/core/configuration.rb:1181:in requires=' from /home/krimsonkla/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/rspec-core-3.2.3/lib/rspec/core/configuration_options.rb:110:inblock in process_options_into'
from /home/krimsonkla/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/rspec-core-3.2.3/lib/rspec/core/configuration_options.rb:109:in each' from /home/krimsonkla/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/rspec-core-3.2.3/lib/rspec/core/configuration_options.rb:109:inprocess_options_into'
from /home/krimsonkla/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/rspec-core-3.2.3/lib/rspec/core/configuration_options.rb:22:in configure' from /home/krimsonkla/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/rspec-core-3.2.3/lib/rspec/core/runner.rb:96:insetup'
from /home/krimsonkla/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/rspec-core-3.2.3/lib/rspec/core/runner.rb:85:in run' from /home/krimsonkla/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/rspec-core-3.2.3/lib/rspec/core/runner.rb:70:inrun'
from /home/krimsonkla/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/rspec-core-3.2.3/lib/rspec/core/runner.rb:38:in invoke' from /home/krimsonkla/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/rspec-core-3.2.3/exe/rspec:4:in<top (required)>'
from /home/krimsonkla/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/activesupport-4.2.1/lib/active_support/dependencies.rb:268:in load' from /home/krimsonkla/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/activesupport-4.2.1/lib/active_support/dependencies.rb:268:inblock in load'
from /home/krimsonkla/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/activesupport-4.2.1/lib/active_support/dependencies.rb:240:in load_dependency' from /home/krimsonkla/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/activesupport-4.2.1/lib/active_support/dependencies.rb:268:inload'
from /home/krimsonkla/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/spring-commands-rspec-1.0.4/lib/spring/commands/rspec.rb:18:in call' from /home/krimsonkla/.rbenv/versions/2.2.1/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:inrequire'
from /home/krimsonkla/.rbenv/versions/2.2.1/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in require' from -e:1:in

'

Mock Generation Fails when there is no ActiveRecord::Base class

In config/initializers/active_mocker.rb

 ActiveMocker.configure do |config|
   config.model_base_classes = %w[ ActiveRecord::Base ]
 end

If a model has a base class that still behaves like ActiveRecord but doesn't directly inherit from it add it
to the model_base_classes array.

This is on master branch, test it out and see if there are any edge cases.

Project name

You may want to change the project name to follow http://xunitpatterns.com/Test%20Double%20Patterns.html

The word "mock" implies to run some verification at some point ( expect(mock).to receive(:method).

Looking at the README, it sounds like you have built a library to build fake active record models. I suggest something like active_record_doubles.

Some mechanism for ignoring DSL code in classes

We have a bunch of classes failing to be generated due to using state_machine and liquid_methods. Both of which provide class level methods to the model class body.

Perhaps there could be a config file for various settings, including skipping over specific class methods in AR class definitions?

I can't imagine how support for something like a state machine gem would be easy and so we will probably just skip using active_mocker for any specs that fail due to that.

Happy to work on a PR for this, if you think it makes sense?

Or do you have any other ideas for an approach that would work?

Can't generate mocks for rails Concerns

I'm using active_mocker version 2.1.3. I ran rake active_mocker:build for all of my models, and the two concerns I have failed to get mocked.

One concern, called RecordChanges is used by a model called Booking. The top of my generated file booking_mock.rb looks like the following:

require("active_mocker/mock")
class BookingMock < ActiveMocker::Base
  created_with("2.1.3")
  # _modules_constants.erb
  prepend(RecordChanges)  # OFFENDING LINE
  #_class_methods.erb
  class << self
  ... # more code here

The mention of RecordChanges causes the error:

Unable to autoload constant Concerns::RecordChanges, expected ~/myproj/app/models/concerns/record_changes.rb to define it

Even though it's declared right there:

# app/models/concerns/record_changes.rb
module RecordChanges
  extend ActiveSupport::Concern
  # ...
end

Why is active_mocker having such a hard time auto-loading / generating mocks out of concerns? I've managed to generate mocks just fine for other models.

Failed models: Concerns::Approval, Concerns::RecordChanges
2 mock(s) out of 21 failed.
To see more/less detail set error_verbosity = 0, 1, 2, 3

Database type 'jsonb' is not a registered type. To register use ActiveRecordSchemaScrapper::Attributes.register_type(name: :jsonb, klass: <RubyClass>)

1. Postgres jsonb type => Hash

Let's say I have a schema.rb that declares a posts table thusly:

  create_table 'vehicle', force: :cascade do |t|
    # ...
    t.jsonb "vehicle_info", default: {}
  end

Here's the error I get when I try rake active_mocker:build:

Database type 'jsonb' is not a registered type.
To register use ActiveRecordSchemaScrapper::Attributes.register_type(name: :jsonb, klass: <RubyClass>)

It's not clear to me where I'm supposed to put ActiveRecordSchemaScrapper::Attributes.register_type(name: :jsonb, klass: <RubyClass>). I think it's active_mocker's job to register these data types prior to trying to scrape schema.rb.

2. Postgres inet type => IPAddr

I have in my schema.rb a field t.inet "current_sign_in_ip".

Naturally, I wanted to map inet to IPAddr (see section 1.10).

I added the following

# active_mocker.rb
ActiveRecordSchemaScrapper::Attributes.register_type(name: :inet, klass: IPAddr)

The mock gets generated, but when I use it in my tests the generated code:

    def types
      @types ||= ActiveMocker::HashProcess.new({ id: Fixnum, email: String, encrypted_password: String, reset_password_token: String, reset_password_sent_at: DateTime, remember_created_at: DateTime, sign_in_count: Fixnum, current_sign_in_at: DateTime, last_sign_in_at: DateTime, current_sign_in_ip: IPAddr, last_sign_in_ip: IPAddr, created_at: DateTime, updated_at: DateTime, first_name: String, last_name: String, phone: String, address1: String, address2: String, postcode: String, state: String, approval_notes: String, role: String, image: String, public_page_id: String, county: String, gender: String, title: String, town: String, city_id: Fixnum, deposit_pence: Fixnum, referral_code: String, authentication_token: String, national_insurance: String, current_mode: String, dvla_points: Fixnum, accident_3_years: Axiom::Types::Boolean, motoring_5_years: Axiom::Types::Boolean, criminal_5_years: Axiom::Types::Boolean, refused_insurance: Axiom::Types::Boolean, special_conditions_notes: String, on_drover_fleet_policy: Axiom::Types::Boolean, uk_resident_since: Date, notes: String, date_of_birth: Date }, method(:build_type)).merge(super)
    end

... gives me a

NameError: uninitialized constant Class::IPAddr

i.e. generated mock is missing a require 'ipaddr'

Fall back for specific database types

Postgresql has JSON, Hstore and there are others that are not supported. Currently if you try to generate a mock from model that is not part of the common types the generation fails. I would like to make an AnyType that would be a fallback. Also provide a way so that a user could extend the types to add their own custom types.

Enable use of alias_method_chain on Relations

Sorry for lack of test isolation case so far. This is on latest master of active_mocker.

In a class Foo with some_aliased_method aliased, a stack trace like this results

undefined method `some_aliased_method' for class `#<Module:0x007fb502d53c98>::Foo'
/Users/markburns/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/activesupport-4.2.6/lib/active_support/core_ext/module/aliasing.rb:32:in `alias_method'
/Users/markburns/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/activesupport-4.2.6/lib/active_support/core_ext/module/aliasing.rb:32:in `alias_method_chain'
/Users/markburns/code/some_rails_app/app/models/foo.rb:337:in `<class:Foo>'
/Users/markburns/code/some_rails_app/app/models/foo.rb:1:in `call'
/Users/markburns/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/dissociated_introspection-0.4.1/lib/dissociated_introspection/eval_sandbox.rb:13:in `module_eval'
/Users/markburns/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/dissociated_introspection-0.4.1/lib/dissociated_introspection/eval_sandbox.rb:13:in `call'
/Users/markburns/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/dissociated_introspection-0.4.1/lib/dissociated_introspection/inspection.rb:90:in `load_sandbox'
/Users/markburns/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/dissociated_introspection-0.4.1/lib/dissociated_introspection/inspection.rb:86:in `get_sandbox_class'
/Users/markburns/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/dissociated_introspection-0.4.1/lib/dissociated_introspection/inspection.rb:15:in `get_class'
/Users/markburns/code/active_mocker/lib/active_mocker/mock_creator.rb:132:in `constants'
(erb):2:in `block in partials'

Improve Documentation

Looking for support in documenting useful things, both in the readme and in the code.

If you find value in the project please consider giving back to the community.

Create an API for custom relation collections

This would use the private api of ::new_relation on a mock. The reason this is private is because I don't want to provide a method that could be used in production code that won't work with ActiveRecord. If someone knows of a good way to do this with the AR api let me know, I would like to copy it when ever I can.

ActiveMocker::Mock.relation(mock: UserMock, collection: [UserMock.new, UserMock.new])

This is helpful when stubbing out sets of queries without having to deal with the global state of a mock. Other advantages would be that this would be thread safe, as the global store is not. This lets you do stuff like run your test in parallel.

undefined method `colorize' for "superclass must be a Class (Module given)":String

On latest master we have the following exception when running bundle exec rake active_mocker:build. Which makes me think there is maybe not enough test coverage.

NoMethodError: undefined method `colorize' for "superclass must be a Class (Module given)":String
/Users/markburns/code/active_mocker/lib/active_mocker/display_errors.rb:36:in `block in display_errors'
/Users/markburns/code/active_mocker/lib/active_mocker/display_errors.rb:32:in `each'
/Users/markburns/code/active_mocker/lib/active_mocker/display_errors.rb:32:in `display_errors'
/Users/markburns/code/active_mocker/lib/active_mocker/generate.rb:26:in `call'
/Users/markburns/code/active_mocker/lib/active_mocker/public_methods.rb:31:in `create_mocks'
/Users/markburns/code/active_mocker/lib/active_mocker/task.rake:12:in `block (2 levels) in <top (required)>'

missing forwardable dependency

with ruby 2.5.5 I am getting errors as follows:

Failure/Error: require "active_mocker/rspec_helper"

NameError:
  uninitialized constant #<Class:ActiveMocker::LoadedMocks>::Forwardable
# ./spec/spec_helper.rb:11:in `require'
# ./spec/spec_helper.rb:11:in `<top (required)>'
# ./spec/rails_helper.rb:3:in `require'
# ./spec/rails_helper.rb:3:in `<top (required)>'
# ./spec/requests/updates_spec.rb:1:in `require'
# ./spec/requests/updates_spec.rb:1:in `<top (required)>'

when running bundle exec rspec. It seems like the forwardable gem is missing from the Gemfile/.gemspec?

ruby -e "require 'rspec';require 'active_mocker/rspec_helper'" fails with

Traceback (most recent call last):
	10: from -e:1:in `<main>'
	 9: from /Users/svanzoest/.rbenv/versions/2.5.5/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:39:in `require'
	 8: from /Users/svanzoest/.rbenv/versions/2.5.5/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:135:in `rescue in require'
	 7: from /Users/svanzoest/.rbenv/versions/2.5.5/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:135:in `require'
	 6: from /Users/svanzoest/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/active_mocker-2.6.0/lib/active_mocker/rspec_helper.rb:2:in `<top (required)>'
	 5: from /Users/svanzoest/.rbenv/versions/2.5.5/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:70:in `require'
	 4: from /Users/svanzoest/.rbenv/versions/2.5.5/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:70:in `require'
	 3: from /Users/svanzoest/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/active_mocker-2.6.0/lib/active_mocker/loaded_mocks.rb:4:in `<top (required)>'
	 2: from /Users/svanzoest/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/active_mocker-2.6.0/lib/active_mocker/loaded_mocks.rb:5:in `<module:ActiveMocker>'
	 1: from /Users/svanzoest/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/active_mocker-2.6.0/lib/active_mocker/loaded_mocks.rb:6:in `<class:LoadedMocks>'
/Users/svanzoest/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/active_mocker-2.6.0/lib/active_mocker/loaded_mocks.rb:7:in `singleton class': uninitialized constant #<Class:ActiveMocker::LoadedMocks>::Forwardable (NameError)

Adding forwardable seems to fix it.
ruby -e "require 'forwardable';require 'rspec';require 'active_mocker/rspec_helper'"

Generator fails for STI models

I'm seeing this issue when running the generator:

NoMethodError: undefined method `get_named_scopes' for #<Class:0x00000108b8e0f8>
~/.rvm/gems/ruby-2.1.0@gemset/gems/activerecord-4.1.1/lib/active_record/dynamic_matchers.rb:26:in `method_missing'
~/.rvm/gems/ruby-2.1.0@gemset/bundler/gems/active_mocker-2cf8101aebc2/lib/active_mocker/model_reader.rb:61:in `scopes'
~/.rvm/gems/ruby-2.1.0@gemset/bundler/gems/active_mocker-2cf8101aebc2/lib/active_mocker/model_reader.rb:65:in `scopes_with_arguments'
~/.rvm/gems/ruby-2.1.0@gemset/bundler/gems/active_mocker-2cf8101aebc2/lib/active_mocker/model_schema/generate.rb:80:in `build_methods'
~/.rvm/gems/ruby-2.1.0@gemset/bundler/gems/active_mocker-2cf8101aebc2/lib/active_mocker/model_schema/generate.rb:50:in `block in run'
~/.rvm/gems/ruby-2.1.0@gemset/bundler/gems/active_mocker-2cf8101aebc2/lib/active_mocker/model_schema/generate.rb:36:in `map'
~/.rvm/gems/ruby-2.1.0@gemset/bundler/gems/active_mocker-2cf8101aebc2/lib/active_mocker/model_schema/generate.rb:36:in `run'
~/.rvm/gems/ruby-2.1.0@gemset/bundler/gems/active_mocker-2cf8101aebc2/lib/active_mocker/generate.rb:43:in `generate_model_schema'
~/.rvm/gems/ruby-2.1.0@gemset/bundler/gems/active_mocker-2cf8101aebc2/lib/active_mocker/generate.rb:66:in `create_template'
~/.rvm/gems/ruby-2.1.0@gemset/bundler/gems/active_mocker-2cf8101aebc2/lib/active_mocker/generate.rb:23:in `initialize'
~/.rvm/gems/ruby-2.1.0@gemset/bundler/gems/active_mocker-2cf8101aebc2/lib/active_mocker/task.rake:4:in `new'
~/.rvm/gems/ruby-2.1.0@gemset/bundler/gems/active_mocker-2cf8101aebc2/lib/active_mocker/task.rake:4:in `block (2 levels) in <top (required)>'
~/.rvm/gems/ruby-2.1.0@gemset/bin/ruby_executable_hooks:15:in `eval'
~/.rvm/gems/ruby-2.1.0@gemset/bin/ruby_executable_hooks:15:in `<main>'

The model in question is setup with single-table inheritance and looks like this:

class ParentModel < ActiveRecord::Base
  # has a `type` column in DB
end

class ChildModel < ParentModel

end

The generator fails while processing ChildModel.

Calling scoped method that has not been stubbed raises incorrect error.

TypeError: backtrace must be Array of String
/Users/zeisler/.rvm/gems/ruby-2.1.5/gems/active_mocker-1.8.3/lib/active_mocker/mock/mock_abilities.rb:30:in set_backtrace' /Users/zeisler/.rvm/gems/ruby-2.1.5/gems/active_mocker-1.8.3/lib/active_mocker/mock/mock_abilities.rb:30:inraise'
/Users/zeisler/.rvm/gems/ruby-2.1.5/gems/active_mocker-1.8.3/lib/active_mocker/mock/mock_abilities.rb:30:in is_implemented' /Users/zeisler/.rvm/gems/ruby-2.1.5/gems/active_mocker-1.8.3/lib/active_mocker/mock/mock_abilities.rb:60:incall_mock_method'

module Scopes
    include ActiveMocker::Mock::Base::Scopes

    def primary_fees(chapter_type)
      ActiveMocker::LoadedMocks.find('_').send(:call_mock_method, '_', chapter_type)
    end

This Api was not updated when primary stub methods were changed. Here df4dea9#diff-88dfa03d28830b0a291f1e9d53fc343a

Support polymorphic associations

Usage of polymorphic associations results in an exception

e.g.

Missing model Bar for association Foo.belongs_to :bar, polymorphic: true

Constant values assigned to non sudo primitives objects

class UserMock < ActiveMocker::Base
  RELATION = #<User ...>
end

When values are sudo primitives objects calling #inspect results in syntacticly correct assignment, but in many cases calling #inspect will not result in a correct assignment.

Possible solution when it is not a string, a number, or other printable object then replace it with a string "ActiveMocker can not determine the value, if needed stub this const with a valid test value"

Calling update on a record should call save as well.

ActiveRecord Code

# Updates the attributes of the model from the passed-in hash and saves the
    # record, all wrapped in a transaction. If the object is invalid, the saving
    # will fail and false will be returned.
def update(attributes)
      # The following transaction covers any possible database side-effects of the
      # attributes assignment. For example, setting the IDs of a child collection
  with_transaction_returning_status do
    assign_attributes(attributes)
    save
  end
end

ActiveMocker::Base Code

def update(attributes = {})
  assign_attributes(attributes)
end

Calling update should save the record if it was not already persisted.

Feedback on adding a pass through select method.

I am considering this in the next release. I feel a little hesitant because it could lead to some production code bugs. For example User.select(:name).first.email will raise an error in ActiveRecord but it would work in ActiveMocker. I considered making it work the same but the complexity is high because the record/object would need to look up to it's owner and see what methods it can emit, but records can be owned by many collections.

# Warning Use method at own risk!
# Method will pass through to the next chained method. The attributes not selected will be available and ActiveRecord
# would raise 'missing attribute' error, while ActiveMocker will not.
#
#   User.select(:name, :email) => User
#
def self.select(*args)
  self
end

Support first_or_create on collection

From what I have seen Collection, Relations and Scopes don't currently support the first_or_create method.

I'm doing a workaround for now, but wanted to make a note of the issue so I don't forget to give proper repro steps.

Ignore modules

Usage of modules results in e.g.

SomeNamespace::SomeModule has the following errors:
undefined method `to_a' for false:FalseClass
Did you mean?  to_yaml
               to_param
               to_s
warn
undefined method `to_a' for false:FalseClass
Did you mean?  to_yaml
               to_param
               to_s

Using a default value for decimal data types results in a failing mock class generated in 2.2.3 and a mock class syntax error in version 2.0.0

A table using a default value for a decimal column results in an error when building mock files. I tested this by creating a new rails app with the following schema:

create_table "test_data", force: :cascade do |t|
    t.datetime "created_at",                                                                         null: false
    t.datetime "updated_at",                                                                         null: false
    t.decimal  "decimal_default_neg",                        precision: 19, scale: 6, default: -1.0
    t.decimal  "decimal_default_pos",                        precision: 19, scale: 6, default: 1.0
    t.decimal  "decimal_no_default",                         precision: 19, scale: 6
    t.decimal  "decimal_no_precision_no_scale_with_default",                          default: 1.0
    t.integer  "integer_default_neg",                                                 default: -1
    t.integer  "integer_default_pos",                                                 default: 2
end

On active_mocker v 2.2.3, running rake_active_mocker:buildwith verbosity 3 results in the following: (full stack trace omitted)

TestDatum has the following errors:
unexpected token kEND
warn
unexpected token kEND
/Users/john/.rvm/gems/ruby-2.1.7/gems/parser-2.3.1.2/lib/parser/diagnostic/engine.rb:71:in `process'

Parser::SyntaxError
Error Summary
errors: 0, warn: 1, info: 0
Failed models: TestDatum
1 mock(s) out of 1 failed.

Downgrading to version 2.0.0 results in a successfully generated mock class. However, the #attributes method is partially commented out due to #<BigDecimal> being inserted as a value for decimal attributes, resulting in a syntax error.

def attributes
  @attributes ||= HashWithIndifferentAccess.new({"id"=>nil, "created_at"=>nil, "updated_at"=>nil, "decimal_default_neg"=>#<BigDecimal:7fde7db90ab0,'-0.1E1',9(18)>, "decimal_default_pos"=>#<BigDecimal:7fde7dbc3f50,'0.1E1',9(18)>, "decimal_no_default"=>nil, "decimal_no_precision_no_scale_with_default"=>#<BigDecimal:7fde7dbc32d0,'0.1E1',9(18)>, "integer_default_neg"=>-1, "integer_default_pos"=>2}).merge(super)
end

I'm trying to upgrade from 1.8.3, and the mock file generated will correctly show the value in this version:

def attributes
  @attributes ||= HashWithIndifferentAccess.new({"id"=>nil, "created_at"=>nil, "updated_at"=>nil, "decimal_default_neg"=>-1.0, "decimal_default_pos"=>1.0, "decimal_no_default"=>nil, "decimal_no_precision_no_scale_with_default"=>1.0, "integer_default_neg"=>-1, "integer_default_pos"=>2}).merge(super)
end

Less warnings when generating for mixed model and non model files.

Sample output

Generating Mocks |========================================================>>| 100%
Error Summary
errors: 1, warn: 5, info: 0
Failed models: ReportParser::UnderwritingData::ApplicationRecord
96 mock(s) out of 98 failed.
To see more/less detail set error_verbosity = 0, 1, 2, 3

Breaks when `has_many` has a custom scope :-(

Something similar to:

  has_many :posts, -> { order('created_at DESC') }

Give me back a:

E, [2014-10-14T17:10:34.358111 #1562] ERROR -- : Error loading Model: SOME_MODEL
    undefined method `each_pair' for #<Proc:0x007f6f91671ff8@/workspace/SOME_MODEL.rb:4 (lambda)>

I tried looking around the codebase to see if I could be of any help but time ran out. To reproduce the issue with the current specs, just change this line to:

has_many :users, -> { order('created_at DESC') } 

If you need any other information just LMK!

ruby version requirement

README says: Requires Ruby MRI =< 2.1.
bundler says: Gem::InstallError: active_mocker requires Ruby version >= 2.1.0.

Superclass mismatch

This class results in a superclass mismatch error:

class SomeNamespace::SomeClass < ActiveRecord::Base
  self.table_name = 'some_convenient_name'
end
SomeClass has the following errors:
superclass mismatch for class SomeClass
error
superclass mismatch for class SomeClass
/Users/markburns/code/some_rails_app/app/models/some_namespace/some_class.rb:1:in `call'
/Users/markburns/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/dissociated_introspection-0.4.1/lib/dissociated_introspection/eval_sandbox.rb:13:in `module_eval'
/Users/markburns/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/dissociated_introspection-0.4.1/lib/dissociated_introspection/eval_sandbox.rb:13:in `call'
/Users/markburns/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/dissociated_introspection-0.4.1/lib/dissociated_introspection/inspection.rb:90:in `load_sandbox'
/Users/markburns/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/dissociated_introspection-0.4.1/lib/dissociated_introspection/inspection.rb:86:in `get_sandbox_class'
/Users/markburns/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/dissociated_introspection-0.4.1/lib/dissociated_introspection/inspection.rb:15:in `get_class'
/Users/markburns/code/active_mocker/lib/active_mocker/mock_creator.rb:132:in `constants'
(erb):2:in `block in partials'

Minitest support?

Hello, this looks like an awesome project. Our test base is incredibly slow, probably because of all the DB calls. I would like to integrate this tool, but ... we use minitest. Is it possible to use this with minitest?

Ignore Non AR subclasses

I've been trying to get an isolated failing spec for this, but I don't quite fully understand the code and specs of this project yet.

If we have Foo and that inherits from top level class Bar then I'd rather not see any errors or warnings like:

Foo does not inherit from ActiveRecord::Base
Bar is missing a parent class.

Doesn't works with db/structure.sql

Doesn't works with db/structure.sql instead of db/schema.rb

rake active_mocker:build

rake aborted!cks |>>                                                                                               | 0%
NoMethodError: undefined method `class_name' for #<Errno::ENOENT:0x007f4af1480e48>

Errno::ENOENT: No such file or directory @ rb_sysopen - /home/myproject/db/schema.rb

Tasks: TOP => active_mocker:build
(See full trace by running task with --trace)

Generator fails for abstract classes

Thanks for this gem! I'd love to try it out but I'm running into a problem right out of the gate with rake active_mocker:build:

rake aborted!cks |>>                                    
NoMethodError: undefined method `fields' for nil:NilClass
~/.rvm/gems/ruby-2.1.0@gemset/gems/active_mocker-1.6.2/lib/active_mocker/model_schema/generate.rb:41:in `block in run'
~/.rvm/gems/ruby-2.1.0@gemset/gems/active_mocker-1.6.2/lib/active_mocker/model_schema/generate.rb:36:in `map'
~/.rvm/gems/ruby-2.1.0@gemset/gems/active_mocker-1.6.2/lib/active_mocker/model_schema/generate.rb:36:in `run'
~/.rvm/gems/ruby-2.1.0@gemset/gems/active_mocker-1.6.2/lib/active_mocker/generate.rb:43:in `generate_model_schema'
~/.rvm/gems/ruby-2.1.0@gemset/gems/active_mocker-1.6.2/lib/active_mocker/generate.rb:66:in `create_template'
~/.rvm/gems/ruby-2.1.0@gemset/gems/active_mocker-1.6.2/lib/active_mocker/generate.rb:23:in `initialize'
~/.rvm/gems/ruby-2.1.0@gemset/gems/active_mocker-1.6.2/lib/active_mocker/task.rake:4:in `new'
~/.rvm/gems/ruby-2.1.0@gemset/gems/active_mocker-1.6.2/lib/active_mocker/task.rake:4:in `block (2 levels) in <top (required)>'
~/.rvm/gems/ruby-2.1.0@gemset/bin/ruby_executable_hooks:15:in `eval'
~/.rvm/gems/ruby-2.1.0@gemset/bin/ruby_executable_hooks:15:in `<main>'

Seems like the problem is that the class in question isn't backed by a database table, such as:

class ApplicationModel < ActiveRecord::Base
  self.abstract_class = true
end

Poking around, it looks like there is a check in get_table to see if the table is nil, but run still assumes that the table is there. I imagine this might also cause problems with models that use single-table inheritance, which I also have in my project but haven't had a chance to test yet.

I'd be happy to contribute a PR if that would help! 😃

has_one relations are read only

Found case where creating a mock would allow the setting of a has_one relationship. Couldn't find the rails docs for this, but in rails it looks it works as a read only attribute and is not settable. Needs further research and feedback.

mock_instance/class_method block argument only takes local variables.

When using Rspec and mocking a method:
let(:display_name){ "First, Last Name"}

User.mock_instance_method(:display_name){ display_name }

display name is not in scope, to make it work the let must be saved to a local variable. This is unexpected and would be better if the scope local to the block creation location.

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.