- Notifications
You must be signed in to change notification settings - Fork26
Generate mocks from ActiveRecord models for unit tests that run fast because they don’t need to load Rails or a database.
License
zeisler/active_mocker
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
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 seconds374 examples, 0 failures"Mocking ActiveRecord with ActiveMocker" by Envy
- Documentation
- Contact
- Installation
- Setup
- Dependencies
- Usage
- Optional Features
- Mocking Methods
- Managing Mocks
- ActiveRecord supported methods
- Known Limitations
- Inspiration
- Contributing
Ask a question in thechat room.
Add this line to your application's Gemfile:
group:development,:testdogem'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:
$ bundleOr install it yourself as:
$ gem install active_mocker- Tested with Rails 4.x, 5.x, 6.x
- Requires Ruby MRI >= 2.4.x
Seeexample_rails_app for complete setup.
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#db/schema.rbActiveRecord::Schema.define(version:20140327205359)docreate_table"people",force:truedo |t|t.integer"account_id"t.string"first_name",limit:128t.string"last_name",limit:128t.string"address",limit:200t.string"city",limit:100endend
#app/models/person.rbclassPerson <ActiveRecord::Basebelongs_to:accountdefself.bar(name,type=nil)putsnameendend
require'rspec'require'active_mocker/rspec_helper'require'spec/mocks/person_mock'require'spec/mocks/account_mock'describe'Example',active_mocker:truedobeforedoPerson.create# stubbed for PersonMock.createendend
- Assigning the tag
active_mocker:truewill stub any ActiveRecord model Constants for Mock classes in anitor abefore/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)useactive_mocker.find('ClassName'). - Mock state will be cleaned up for you in an
after(:all). To clean state by yourself, useactive_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"
(Afterrake db:migrate is called the mocks will be regenerated.)
#db/schema.rbActiveRecord::Schema.define(version:20140327205359)docreate_table"people",force:truedo |t|t.integer"account_id"t.string"f_name",limit:128t.string"l_name",limit:128t.string"address",limit:200t.string"city",limit:100endend
Person.new(first_name:"Dustin",last_name:"Zeisler")=>#<UnknownAttributeError unknown attribute: first_name >
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.
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)
Enablescreated_at andupdated_at to be updated on save and create
When using "active_mocker/rspec_helper", it deletes all records from all mocks before each example.
When requiring "active_mocker/rspec_helper", and addingactive_mocker: true to thedescribe metadata, these errors will be auto stubbed:
- ActiveRecord::RecordNotFound
- ActiveRecord::RecordNotUnique
- ActiveRecord::UnknownAttributeError
Adding the commentActiveMocker.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: [])classUserdeffull_name"#{first_name} +#{last_name}"endend
Verifying doubles is a stricter alternative to normal doubles that provides guarantees aboutwhat is being verified. When using verifying doubles, RSpec will check if the methodsbeing stubbed are actually present on the underlying object if it is available.rspec-mocks/docs/verifying-doubles
RSpec.configuredo |config|config.mock_framework=:rspecconfig.mock_with:rspecdo |mocks|mocks.verify_doubled_constant_names=truemocks.verify_partial_doubles=trueendend
Person.bar('baz')=>NotImplementedError: ::barisnotImplementedforClass:PersonMock.Tocontinuestubthemethod.allow(Person).toreceive(:bar)do |name,type=nil|"Now implemented with#{name} and#{type}"endPerson.bar('foo','type')=>"Now implemented with foo and type"
(Requires a regeneration of the mocks files.)
#app/models/person.rbclassPerson <ActiveRecord::Basebelongs_to:accountdefself.bar(name)putsnameendend
Person.bar('foo','type')=>ArgumentError:wrongnumberofarguments(2for1)
#app/models/person.rbclassPerson <ActiveRecord::Basebelongs_to:accountdefself.foo(name,type=nil)putsnameendend
allow(Person).toreceive(:bar)do |name,type=nil|"Now implemented with#{name} and#{type}"end=>RSpec::Mocks::MockExpectationError:PersonMockdoes notimplement:bar
- Any locally defined modules will not be included or extended. It can be disabled by
ActiveMocker::Config.disable_modules_and_constants = true
classPerson <ActiveRecord::BaseCONSTANT_VALUE=13end
PersonMock::CONSTANT_VALUE=>13
- 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.
require"active_mocker/rspec_helper"active_mocker.delete_all# Delete all records from loaded mocksactive_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.
SeeDocumentation 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
- A db/schema.rb is not required.
- All schema types are supported and coerced byVirtus. If coercion fails, the passed value will be retained.
- Default value is supported.
- Scale and Precision are not supported.
- 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. Seetest_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 =)).
Thanks to Jeff Olfert for being my original inspiration for this project.
Your contributions are welcome!
- Fork it (http://github.com/zeisler/active_mocker/fork )
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create new Pull Request
About
Generate mocks from ActiveRecord models for unit tests that run fast because they don’t need to load Rails or a database.
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.
Contributors12
Uh oh!
There was an error while loading.Please reload this page.
