├── .document ├── .gitignore ├── .travis.yml ├── CHANGES.markdown ├── Gemfile ├── License.txt ├── README.markdown ├── Rakefile ├── lib ├── sham.rb └── sham │ ├── base.rb │ ├── config.rb │ ├── config │ ├── assign.rb │ ├── attributes.rb │ ├── base.rb │ ├── empty.rb │ ├── hash_options.rb │ ├── no_args.rb │ └── parameters.rb │ ├── data.rb │ ├── lazy.rb │ ├── nested.rb │ ├── shammable.rb │ ├── util.rb │ └── version.rb ├── sham.gemspec └── spec ├── lib └── sham │ ├── base_spec.rb │ ├── config │ ├── assign_spec.rb │ ├── attributes_spec.rb │ ├── empty_spec.rb │ ├── no_args_spec.rb │ └── parameters_spec.rb │ ├── config_spec.rb │ ├── data_spec.rb │ ├── lazy_spec.rb │ ├── nested_spec.rb │ └── util_spec.rb └── root └── sham └── employee.rb /.document: -------------------------------------------------------------------------------- 1 | README.markdown 2 | License.txt 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | pkg/* 3 | *.gem 4 | *.swp 5 | *.rbc 6 | .bundle 7 | .rvmrc 8 | Gemfile.lock 9 | doc 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | dist: trusty 3 | rvm: 4 | - 1.8.7 5 | - 1.9.3 6 | - 2.0.0 7 | - 2.1.10 8 | - 2.2 9 | - 2.3 10 | - 2.4 11 | - 2.5 12 | - 2.6 13 | - 2.7 14 | - jruby-18mode 15 | - jruby-19mode 16 | - jruby-head 17 | -------------------------------------------------------------------------------- /CHANGES.markdown: -------------------------------------------------------------------------------- 1 | # master (unreleased) 2 | 3 | # 2.0.0 4 | 5 | * Use `save!` instead of `save` for sham calls. 6 | 7 | # 1.2.0 8 | 9 | * Add `Sham::Lazy` type. 10 | 11 | # 1.1.0 12 | 13 | * Added `assign` sham configuration. 14 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | gemspec 4 | -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Panayiotis Thomakos 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # Sham 2 | 3 | Lightweight flexible factories for Ruby and Rails testing. 4 | 5 | ## Installation 6 | 7 | gem install sham 8 | 9 | ## Getting Started 10 | 11 | Create a configuration file for any of your models or classes. 12 | 13 | # sham/user.rb 14 | Sham.config(User) do |c| 15 | c.attributes do 16 | { :name => "Sample User" } 17 | end 18 | end 19 | 20 | To load a shams you can either include the configuration file directly, or 21 | define the sham inline in a test file. Sham provides a helper function to 22 | load all files under the sham directory. If you are using Rails you can load 23 | all your shams by adding the following to `config/environments/test.rb`. 24 | 25 | config.after_initialize do 26 | Sham::Config.activate! 27 | end 28 | 29 | If you aren't using Rails you can activate all of your shams by specifying a 30 | configuration path. The following command will load all Ruby files under the 31 | `/my/project/path/sham` directory. 32 | 33 | Sham::Config.activate!('/my/project/path') 34 | 35 | To load all your shams in Cucumber, modify your `features/support/env.rb` file. 36 | 37 | require 'sham' 38 | Sham::Config.activate! 39 | 40 | You can now "sham" your models! When you sham a model it is created with the 41 | default options you specified in the config file. But you can also overwrite 42 | any number of them and add additional attributes on a case-by-case basis. 43 | 44 | User.sham! 45 | User.sham!(:name => "New Name") 46 | User.sham!(:age => 23) 47 | 48 | Sham can also create objects without automatically saving using the `:build` 49 | option. 50 | 51 | user = User.sham!(:build, :name => "I have not been saved") 52 | user.save! 53 | 54 | ## RSpec Example 55 | 56 | The following is an example of an RSpec test for `ActiveRecord` validations. 57 | 58 | # app/models/item.rb 59 | class Item < ActiveRecord::Base 60 | validates_numericality_of :quantity, :greater_than => 0 61 | end 62 | 63 | # sham/item.rb 64 | Sham.config(Item) do |c| 65 | c.attributes do 66 | { :quantity => 1 } 67 | end 68 | end 69 | 70 | # spec/models/item_spec.rb 71 | require 'spec_helper' 72 | require './sham/item' 73 | 74 | describe Item do 75 | it "should not allow items with a negative price" do 76 | item = Item.sham!(:build, :quantity => -1) 77 | item.valid?.should be_false 78 | end 79 | 80 | it "should allow items with a positive quantity" do 81 | item = Item.sham!(:build, :quantity => 10) 82 | item.valid?.should be_true 83 | end 84 | end 85 | 86 | ## Parameter/Argument Shams 87 | 88 | You can also define shams for initializers that take a list of arguments 89 | instead of an attribute hash. For example, if you had a `User` class. 90 | 91 | # lib/user.rb 92 | class User 93 | attr_accessor :first, :last 94 | 95 | def initialize(first, last) 96 | self.first = first 97 | self.last = last 98 | end 99 | end 100 | 101 | You could create a parameter sham like this: 102 | 103 | # sham/user.rb 104 | Sham.config(User) do |c| 105 | c.parameters do 106 | ['John', 'Doe'] 107 | end 108 | end 109 | 110 | And invoke it like this: 111 | 112 | User.sham! 113 | User.sham!('Jane', 'Doe') 114 | 115 | Unlike attribute shams, if arguments are passed to a parameter sham, those 116 | arguments are the only ones passed to the constructor and the parameters are 117 | not merged with the defaults. 118 | 119 | ## Multiple Sham Configurations 120 | 121 | Sometimes you want to be able to configure more than one sham per class. Sham 122 | makes it easy to define alternative configurations by specifying a config name. 123 | 124 | # sham/item.rb 125 | Sham.config(Item, :small) do |c| 126 | c.attributes do 127 | { :weight => 10.0 } 128 | end 129 | end 130 | 131 | Sham.config(Item, :large) do |c| 132 | c.attributes do 133 | { :weight => 100.0 } 134 | end 135 | end 136 | 137 | Alternative sham configurations can be invoked by passing their name into the 138 | `sham!` command. 139 | 140 | Item.sham!(:small, :quantity => 100) 141 | Item.sham!(:large, :build, :quantity => 0) 142 | 143 | ## Assign Shams 144 | 145 | If you have configured mass-assignment protected attributes in Rails, or you 146 | would prefer your object to go through regular instance setters rather than the 147 | initializer, you can use the `assign` configuration. 148 | 149 | # sham/user.rb 150 | Sham.config(User) do |c| 151 | c.assign do 152 | { :name => 'John Doe' } 153 | end 154 | end 155 | 156 | When executing `User.sham!` all attributes will be assigned using the instance 157 | setters instead of the initializer. A `save!` will also be called unless the 158 | `:build` parameters is used. 159 | 160 | User.any_instance.should_receive(:name=).with('Jane Doe') 161 | User.any_instance.should_receive(:save!) 162 | User.sham!(:name => 'Jane Doe') 163 | 164 | ## Empty Shams 165 | 166 | Sometimes you simply want to be able to sham an object without passing any 167 | default options. Sham makes this easy by providing an `empty` configuration. 168 | 169 | # sham/user.rb 170 | Sham.config(User){ |c| c.empty } 171 | 172 | Empty configurations behave just like empty hashes. That means you can simply 173 | pass your own attributes in when shamming the class. 174 | 175 | User.sham! 176 | User.sham!(:name => 'John Doe') 177 | 178 | For parameter based shams you can create empty configurations using the 179 | `no_args` option. 180 | 181 | Sham.config(User){ |c| c.no_args } 182 | 183 | ## Nested Shams 184 | 185 | Sometimes you want one sham to be responsible for creating additional shams when 186 | it is initialized. For instance, a `LineItem` might require an `Item` to be 187 | considered a valid object. Sham makes this kind of nested sham very easy to 188 | configure, and allows you to overwrite the 'sub-object' during initialization. 189 | 190 | # sham/line_item_sham.rb 191 | Sham.config(LineItem) do |c| 192 | c.attributes do 193 | { :item => Sham::Nested.new(Item) } 194 | end 195 | end 196 | 197 | The nested shams will automatically be created and can also be overwritten 198 | during initialization: 199 | 200 | LineItem.sham! 201 | LineItem.sham!(:item => Item.sham!(:weight => 100)) 202 | 203 | ## Lazy Shams 204 | 205 | A more general form of Nested Sham is the Lazy Sham. Lazy Shams only evaluate 206 | their blocks if they are not overwritten. 207 | 208 | # sham/line_item_sham.rb 209 | Sham.config(LineItem) do |c| 210 | c.attributes do 211 | { :item_id => Sham::Lazy.new { Item.sham!.id } } 212 | end 213 | end 214 | 215 | The lazy shams will automatically be evaluated and can also be overwritten 216 | during initialization: 217 | 218 | LineItem.sham! 219 | LineItem.sham!(:item_id => Item.sham!(:weight => 100).id) 220 | 221 | ## Sham Inheritance 222 | 223 | Sham plays well with inheritance. That means shams defined on parent classes 224 | will be available to child classes as well. 225 | 226 | Sham.config(Person) do |c| 227 | c.empty 228 | end 229 | 230 | class Person; end 231 | class Employee < Person; end 232 | 233 | Employee.sham! 234 | 235 | You can also define different shams for your subclasses instead of relying on 236 | the parent object. 237 | 238 | ## Reloading Shams with Spork 239 | 240 | [Spork](https://rubygems.org/gems/spork) is a great gem that creates a 241 | Distributed Ruby environment that you can run your RSpec and Cucumber tests 242 | against. If you are using Rails it is often necessary to re-load your models and 243 | controllers between Spork test runs so that the Spork DRB picks up your latest 244 | model changes. This is usually accomplished using a Spork 'each run' block. This 245 | block of code gets executed before each test run. If you want to be able to 246 | reload your shams with Spork all you need to do is add a 247 | `Sham::Config.activate!` line to this block after you have re-loaded your models 248 | and controllers. 249 | 250 | Spork.each_run do 251 | Sham::Config.activate! 252 | end if Spork.using_spork? 253 | 254 | This change will cause sham to be re-loaded so that you can continue to use it 255 | with Spork. If you take this approach it's important to remove the call to 256 | `Sham::Config.activate!` from your `test.rb` file. 257 | 258 | ## Build Status [![Build Status](https://app.travis-ci.com/panthomakos/sham.svg?branch=master)](https://secure.travis-ci.org/panthomakos/sham.png) 259 | 260 | ## Code Quality [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/panthomakos/sham) 261 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler' 2 | Bundler::GemHelper.install_tasks 3 | 4 | require 'rspec/core/rake_task' 5 | 6 | RSpec::Core::RakeTask.new(:spec) 7 | 8 | task :default => :spec 9 | -------------------------------------------------------------------------------- /lib/sham.rb: -------------------------------------------------------------------------------- 1 | module Sham; end 2 | 3 | require 'sham/data' 4 | require 'sham/util' 5 | require 'sham/base' 6 | require 'sham/nested' 7 | require 'sham/lazy' 8 | require 'sham/config' 9 | require 'sham/shammable' 10 | 11 | -------------------------------------------------------------------------------- /lib/sham/base.rb: -------------------------------------------------------------------------------- 1 | module Sham 2 | class Base 3 | def initialize klass, *args 4 | @klass = klass 5 | @args = args 6 | end 7 | 8 | def sham! 9 | @klass.sham!(*@args) 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/sham/config.rb: -------------------------------------------------------------------------------- 1 | require 'sham/shammable' 2 | require 'sham/config/assign' 3 | require 'sham/config/attributes' 4 | require 'sham/config/parameters' 5 | require 'sham/config/empty' 6 | require 'sham/config/no_args' 7 | 8 | module Sham 9 | class << self 10 | def config klass, name = :default 11 | unless (class << klass; self; end).include?(Sham::Shammable) 12 | klass.extend(Sham::Shammable) 13 | end 14 | yield(Sham::Config.new(klass, name)) if block_given? 15 | end 16 | end 17 | 18 | class Config 19 | def self.activate! root = nil 20 | root = Rails.root if root.nil? && defined?(Rails.root) 21 | root = File.join(root, 'sham', '**', '*.rb') 22 | Dir[root].each{ |f| load(f) } 23 | end 24 | 25 | def initialize klass, name 26 | @klass = klass 27 | @name = name 28 | end 29 | 30 | def assign(&config) 31 | @klass.add_sham_config(@name, Sham::Config::Assign.new(config)) 32 | end 33 | 34 | def attributes(&config) 35 | @klass.add_sham_config(@name, Sham::Config::Attributes.new(config)) 36 | end 37 | 38 | def parameters(&config) 39 | @klass.add_sham_config(@name, Sham::Config::Parameters.new(config)) 40 | end 41 | 42 | def empty 43 | @klass.add_sham_config(@name, Sham::Config::Empty.new) 44 | end 45 | 46 | def no_args 47 | @klass.add_sham_config(@name, Sham::Config::NoArgs.new) 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /lib/sham/config/assign.rb: -------------------------------------------------------------------------------- 1 | require 'sham/config/base' 2 | require 'sham/config/hash_options' 3 | 4 | module Sham 5 | class Config 6 | class Assign < Base 7 | include HashOptions 8 | 9 | def initialize(config) 10 | @config = config 11 | end 12 | 13 | def sham(build = false) 14 | object = super(true) 15 | 16 | @options.each do |key, value| 17 | object.public_send("#{key}=", value) 18 | end 19 | 20 | if !build && object.respond_to?(:save!) 21 | object.save! 22 | end 23 | 24 | object 25 | end 26 | 27 | def args 28 | [] 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/sham/config/attributes.rb: -------------------------------------------------------------------------------- 1 | require 'sham/config/base' 2 | require 'sham/config/hash_options' 3 | 4 | module Sham 5 | class Config 6 | class Attributes < Base 7 | include HashOptions 8 | 9 | def initialize(config) 10 | @config = config 11 | end 12 | 13 | def args 14 | [@options] 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/sham/config/base.rb: -------------------------------------------------------------------------------- 1 | require 'sham/nested' 2 | require 'sham/lazy' 3 | 4 | module Sham 5 | class Config 6 | class Base 7 | def object(klass) 8 | @klass = klass 9 | 10 | self 11 | end 12 | 13 | def sham(build = false) 14 | if build || !@klass.respond_to?(:create) 15 | @klass.new(*args) 16 | else 17 | @klass.create(*args) 18 | end 19 | end 20 | 21 | private 22 | 23 | def parse! value 24 | case value 25 | when Array then value.map{ |k| parse!(k) } 26 | when Hash then value.map{ |k,v| [k, parse!(v)] }.to_h 27 | when Sham::Nested then value.sham! 28 | when Sham::Lazy then value.sham! 29 | else value 30 | end 31 | end 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/sham/config/empty.rb: -------------------------------------------------------------------------------- 1 | require 'sham/config/base' 2 | require 'sham/util' 3 | 4 | module Sham 5 | class Config 6 | class Empty < Base 7 | def options(*args) 8 | @options = ::Sham::Util.extract_options!(args) 9 | 10 | self 11 | end 12 | 13 | def args 14 | [@options] 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/sham/config/hash_options.rb: -------------------------------------------------------------------------------- 1 | require 'sham/util' 2 | 3 | module Sham 4 | class Config 5 | module HashOptions 6 | def options(*args) 7 | @options = ::Sham::Util.extract_options!(args) 8 | 9 | @config.call.each do |key, value| 10 | @options[key] = parse!(value) unless @options.has_key?(key) 11 | end 12 | 13 | self 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/sham/config/no_args.rb: -------------------------------------------------------------------------------- 1 | require 'sham/config/base' 2 | require 'sham/util' 3 | 4 | module Sham 5 | class Config 6 | class NoArgs < Base 7 | def options(*args) 8 | @args = args 9 | 10 | self 11 | end 12 | 13 | def args 14 | @args 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/sham/config/parameters.rb: -------------------------------------------------------------------------------- 1 | require 'sham/config/base' 2 | 3 | module Sham 4 | class Config 5 | class Parameters < Base 6 | def initialize(config) 7 | @config = config 8 | end 9 | 10 | def options(*args) 11 | @args = args 12 | 13 | if @args.empty? 14 | @config.call.each do |arg| 15 | @args << parse!(arg) 16 | end 17 | end 18 | 19 | self 20 | end 21 | 22 | def args 23 | @args 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/sham/data.rb: -------------------------------------------------------------------------------- 1 | require 'securerandom' 2 | 3 | module Sham 4 | class << self 5 | def string! len = 5 6 | ::SecureRandom.hex(len) 7 | end 8 | 9 | def integer! top = 100 10 | ::SecureRandom.random_number(top) 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/sham/lazy.rb: -------------------------------------------------------------------------------- 1 | module Sham 2 | class Lazy 3 | def initialize(&block) 4 | @block = block 5 | end 6 | 7 | def sham! 8 | @block.call 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/sham/nested.rb: -------------------------------------------------------------------------------- 1 | require 'sham/base' 2 | 3 | module Sham 4 | Nested = Sham::Base 5 | end 6 | -------------------------------------------------------------------------------- /lib/sham/shammable.rb: -------------------------------------------------------------------------------- 1 | require 'sham/util' 2 | 3 | module Sham 4 | module Shammable 5 | def add_sham_config(name, config) 6 | @sham_configs ||= {} 7 | @sham_configs[name] = config 8 | end 9 | 10 | def sham_config(name) 11 | if @sham_configs && @sham_configs.has_key?(name) 12 | @sham_configs[name] 13 | elsif superclass.respond_to?(:sham_config) 14 | superclass.sham_config(name) 15 | end 16 | end 17 | 18 | def sham!(*args) 19 | build = extract_build!(args) 20 | type = extract_type!(args) || :default 21 | build ||= extract_build!(args) 22 | 23 | sham_config(type).object(self).options(*args).sham(build == :build) 24 | end 25 | 26 | alias :sham_alternate! :sham! 27 | 28 | private 29 | 30 | def extract_build!(args) 31 | args.shift if args.first == :build 32 | end 33 | 34 | def extract_type!(args) 35 | args.shift if sham_config(args.first) 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/sham/util.rb: -------------------------------------------------------------------------------- 1 | module Sham 2 | module Util 3 | def self.extract_options!(ary) 4 | ary.last.is_a?(::Hash) ? ary.pop : {} 5 | end 6 | 7 | def self.constantize(word) 8 | unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ word 9 | raise NameError, "#{word.inspect} is not a valid constant name!" 10 | end 11 | 12 | Object.module_eval("::#{$1}", __FILE__, __LINE__) 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/sham/version.rb: -------------------------------------------------------------------------------- 1 | module Sham 2 | VERSION = "2.0.0" 3 | end 4 | -------------------------------------------------------------------------------- /sham.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $:.push File.expand_path("../lib", __FILE__) 3 | require "sham/version" 4 | 5 | Gem::Specification.new do |s| 6 | s.name = "sham" 7 | s.version = Sham::VERSION 8 | s.platform = Gem::Platform::RUBY 9 | s.authors = ["Pan Thomakos"] 10 | s.email = ["pan.thomakos@gmail.com"] 11 | s.homepage = "http://github.com/panthomakos/sham" 12 | s.summary = "sham-#{Sham::VERSION}" 13 | s.description = %q{Lightweight flexible factories for Ruby and Rails testing.} 14 | 15 | s.rubyforge_project = "sham" 16 | 17 | s.files = `git ls-files`.split("\n") 18 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 19 | s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } 20 | s.extra_rdoc_files = ["README.markdown", "License.txt"] 21 | s.rdoc_options = ["--charset=UTF-8"] 22 | s.require_paths = ["lib"] 23 | 24 | s.add_development_dependency('rspec') 25 | s.add_development_dependency('rake') 26 | end 27 | -------------------------------------------------------------------------------- /spec/lib/sham/base_spec.rb: -------------------------------------------------------------------------------- 1 | require 'sham/base' 2 | 3 | describe Sham::Base do 4 | let(:klass){ double } 5 | 6 | it 'should call sham! on the class' do 7 | klass.should_receive(:sham!) 8 | described_class.new(klass).sham! 9 | end 10 | 11 | it 'should pass sham options' do 12 | options = double 13 | klass.should_receive(:sham!).with(options) 14 | described_class.new(klass, options).sham! 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /spec/lib/sham/config/assign_spec.rb: -------------------------------------------------------------------------------- 1 | require 'sham/config/assign' 2 | 3 | describe Sham::Config::Assign do 4 | before(:all) do 5 | Object.send(:remove_const, :AssignTester) if defined?(AssignTester) 6 | class AssignTester; end 7 | end 8 | 9 | let(:id){ double } 10 | let(:options){ { :id => id } } 11 | let(:subject){ described_class.new(lambda{ options }) } 12 | let(:config){ subject.object(AssignTester) } 13 | let(:instance){ double.as_null_object } 14 | 15 | before{ AssignTester.stub(:new){ instance } } 16 | 17 | it 'sets default options' do 18 | instance.should_receive(:public_send).with('id=', id) 19 | config.options.sham 20 | end 21 | 22 | it 'prioritizes passed options' do 23 | instance.should_receive(:public_send).with('id=', 2) 24 | config.options(:id => 2).sham 25 | end 26 | 27 | it 'merges passed options' do 28 | instance.should_receive(:public_send).with('name=', 'A') 29 | config.options(:name => 'A').sham 30 | end 31 | 32 | it 'parses default options' do 33 | subject.should_receive(:parse!).with(id) 34 | config.options.sham 35 | end 36 | 37 | it 'does not parse passed options' do 38 | subject.should_receive(:parse!).with(2).never 39 | config.options(:id => 2).sham 40 | end 41 | 42 | it 'calls save if the instance responds to save' do 43 | instance.stub(:respond_to?).with(:save!){ true } 44 | instance.should_receive(:save!) 45 | config.options.sham 46 | end 47 | 48 | it 'does not call save on build' do 49 | instance.should_recieve(:save!).never 50 | config.options.sham(true) 51 | end 52 | 53 | it 'does not call save if it does not respond to save' do 54 | instance.stub(:respond_to?).with(:save!){ false } 55 | instance.should_receive(:save!).never 56 | config.options.sham 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /spec/lib/sham/config/attributes_spec.rb: -------------------------------------------------------------------------------- 1 | require 'sham/config/attributes' 2 | 3 | describe Sham::Config::Attributes do 4 | before(:all) do 5 | Object.send(:remove_const, :AttributesTester) if defined?(AttributesTester) 6 | class AttributesTester 7 | attr_accessor :id 8 | 9 | def initialize(options) 10 | self.id = options[:id] 11 | end 12 | end 13 | end 14 | 15 | let(:id){ double } 16 | let(:options){ { :id => id } } 17 | let(:subject){ described_class.new(lambda{ options }) } 18 | let(:config){ subject.object(AttributesTester) } 19 | 20 | it 'passes default options' do 21 | AttributesTester.should_receive(:new).with(options) 22 | config.options.sham 23 | end 24 | 25 | it 'prioritizes passed options' do 26 | AttributesTester.should_receive(:new).with({ :id => 2 }) 27 | config.options(:id => 2).sham 28 | end 29 | 30 | it 'merges passed options' do 31 | AttributesTester.should_receive(:new).with({ :id => id, :name => 'A' }) 32 | config.options(:name => 'A').sham 33 | end 34 | 35 | it 'parses default options' do 36 | subject.should_receive(:parse!).with(id) 37 | config.options.sham 38 | end 39 | 40 | it 'does not parse passed options' do 41 | subject.should_receive(:parse!).with(2).never 42 | config.options(:id => 2).sham 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /spec/lib/sham/config/empty_spec.rb: -------------------------------------------------------------------------------- 1 | require 'sham/config/empty' 2 | 3 | describe Sham::Config::Empty do 4 | before(:all) do 5 | Object.send(:remove_const, :EmptyTester) if defined?(EmptyTester) 6 | class EmptyTester 7 | attr_accessor :id 8 | 9 | def initialize(options) 10 | self.id = options[:id] 11 | end 12 | end 13 | end 14 | 15 | let(:config){ subject.object(EmptyTester) } 16 | 17 | it 'passes an empty hash by default' do 18 | EmptyTester.should_receive(:new).with({}) 19 | config.options.sham 20 | end 21 | 22 | it 'allows passed options' do 23 | EmptyTester.should_receive(:new).with(:id => 1) 24 | config.options(:id => 1).sham 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /spec/lib/sham/config/no_args_spec.rb: -------------------------------------------------------------------------------- 1 | require 'sham/config/no_args' 2 | 3 | describe Sham::Config::NoArgs do 4 | before(:all) do 5 | Object.send(:remove_const, :NoArgsTester) if defined?(NoArgsTester) 6 | class NoArgsTester 7 | attr_accessor :name 8 | 9 | def initialize(name = nil) 10 | self.name = name 11 | end 12 | end 13 | end 14 | 15 | let(:config){ subject.object(NoArgsTester) } 16 | 17 | it 'does not pass parameters by default' do 18 | NoArgsTester.should_receive(:new).with(no_args) 19 | config.options.sham 20 | end 21 | 22 | it 'allows passed parameters' do 23 | NoArgsTester.should_receive(:new).with(1) 24 | config.options(1).sham 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /spec/lib/sham/config/parameters_spec.rb: -------------------------------------------------------------------------------- 1 | require 'sham/config/parameters' 2 | 3 | describe Sham::Config::Parameters do 4 | before(:all) do 5 | Object.send(:remove_const, :ParamTester) if defined?(ParamTester) 6 | class ParamTester 7 | attr_accessor :first, :last 8 | 9 | def initialize(first, last) 10 | self.first = first 11 | self.last = last 12 | end 13 | end 14 | end 15 | 16 | let(:first){ double } 17 | let(:last){ double } 18 | let(:params){ [first, last] } 19 | let(:subject){ described_class.new(lambda{ params }) } 20 | let(:config){ subject.object(ParamTester) } 21 | 22 | it 'passes default options' do 23 | ParamTester.should_receive(:new).with(*params) 24 | config.options.sham 25 | end 26 | 27 | it 'prioritizes passed options' do 28 | ParamTester.should_receive(:new).with('A', 'B') 29 | config.options('A', 'B').sham 30 | end 31 | 32 | it 'parses default options' do 33 | subject.should_receive(:parse!).with(first) 34 | subject.should_receive(:parse!).with(last) 35 | config.options.sham 36 | end 37 | 38 | it 'does not parse passed options' do 39 | subject.should_receive(:parse!).with(1).never 40 | subject.should_receive(:parse!).with(2).never 41 | config.options(1, 2).sham 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /spec/lib/sham/config_spec.rb: -------------------------------------------------------------------------------- 1 | require 'sham/config' 2 | 3 | describe Sham::Config do 4 | let(:parent) do 5 | Object.send(:remove_const, :Parent) if defined?(Parent) 6 | class Parent; end 7 | Parent 8 | end 9 | let(:child) do 10 | Object.send(:remove_const, :Child) if defined?(Child) 11 | class Child < parent; end 12 | Child 13 | end 14 | 15 | context '#activate!' do 16 | it 'activates shams in the root directory' do 17 | begin 18 | expect { 19 | described_class.activate!('spec/root') 20 | }.to change{ defined?(Employee) }.from(nil).to('constant') 21 | ensure 22 | Object.send(:remove_const, :Employee) rescue nil 23 | end 24 | end 25 | end 26 | 27 | context '#config' do 28 | it 'extends the class with Sham::Shammable' do 29 | expect { Sham.config(parent) } \ 30 | .to change{ (class << parent; self; end).include?(Sham::Shammable) } \ 31 | .from(false).to(true) 32 | end 33 | 34 | it 'only extends with Sham::Shammable once' do 35 | Sham.config(parent) 36 | parent.should_receive(:extend).never 37 | 38 | Sham.config(parent, :alternate) 39 | end 40 | 41 | it 'defines sham! on the class' do 42 | expect { Sham.config(parent) } \ 43 | .to change{ parent.respond_to?(:sham!) }.from(false).to(true) 44 | end 45 | 46 | it 'defines sham_alternate! on the class' do 47 | expect { Sham.config(parent) } \ 48 | .to change{ parent.respond_to?(:sham_alternate!) } \ 49 | .from(false).to(true) 50 | end 51 | end 52 | 53 | context 'configured sham' do 54 | it 'should be individual to every class' do 55 | class A; end 56 | class B; end 57 | 58 | a = { :attribute => 'a' } 59 | b = { :attribute => 'b' } 60 | 61 | Sham.config(A){ |c| c.attributes{ a } } 62 | Sham.config(B){ |c| c.attributes{ b } } 63 | 64 | A.should_receive(:new).with(a) 65 | B.should_receive(:new).with(b) 66 | 67 | A.sham! 68 | B.sham! 69 | end 70 | 71 | it 'carries over to subclasses' do 72 | Sham.config(parent) 73 | child.should respond_to(:sham!) 74 | end 75 | 76 | it 'allows subclasses to define their own' do 77 | Sham.config(parent){ |c| c.empty } 78 | Sham.config(child){ |c| c.empty } 79 | 80 | parent.sham_config(:default) \ 81 | .should_not == child.sham_config(:default) 82 | end 83 | 84 | it 'defaults to calling #new when #create is not present' do 85 | Sham.config(parent){ |c| c.empty } 86 | 87 | parent.should_receive(:new).once 88 | 89 | parent.sham! 90 | end 91 | 92 | it 'allows shams to be built instead of created' do 93 | Sham.config(parent){ |c| c.empty } 94 | parent.should_receive(:create).never 95 | parent.should_receive(:new).once 96 | 97 | parent.sham!(:build) 98 | end 99 | 100 | it 'creates shams by default' do 101 | Sham.config(parent){ |c| c.empty } 102 | parent.stub(:create) 103 | parent.should_receive(:create).once 104 | 105 | parent.sham! 106 | end 107 | 108 | context 'alternative shams' do 109 | let(:other){ { :id => 1 } } 110 | 111 | before do 112 | Sham.config(parent){ |c| c.empty } 113 | Sham.config(parent, :other){ |c| c.attributes{ other } } 114 | end 115 | 116 | it 'builds them' do 117 | parent.should_receive(:new).with(other) 118 | 119 | parent.sham!(:build, :other) 120 | end 121 | 122 | it 'creates them' do 123 | parent.should_receive(:create).with(other) 124 | 125 | parent.sham!(:other) 126 | end 127 | end 128 | 129 | context 'nested shams' do 130 | before do 131 | parent.stub(:create) 132 | 133 | Sham.config(parent) do |c| 134 | c.attributes do 135 | { :child => Sham::Nested.new(child) } 136 | end 137 | end 138 | end 139 | 140 | it 'calls them' do 141 | child.should_receive(:sham!) 142 | 143 | parent.sham! 144 | end 145 | 146 | it 'prefers passed options' do 147 | other_child = double 148 | parent.should_receive(:create).with({ :child => other_child }) 149 | parent.sham!(:child => other_child) 150 | end 151 | end 152 | 153 | context 'lazy shams' do 154 | before do 155 | parent.stub(:create) 156 | 157 | Sham.config(parent) do |c| 158 | c.attributes do 159 | { :child => Sham::Lazy.new { child } } 160 | end 161 | end 162 | end 163 | 164 | it 'evaluates the block' do 165 | parent.should_receive(:create).with({ :child => child }) 166 | parent.sham! 167 | end 168 | 169 | it 'prefers passed options' do 170 | other_child = double 171 | parent.should_receive(:create).with({ :child => other_child }) 172 | parent.sham!(:child => other_child) 173 | end 174 | end 175 | end 176 | 177 | it 'configures assign shams' do 178 | Sham.config(parent) do |c| 179 | c.assign{ { :first => 'first' } } 180 | end 181 | 182 | instance = double 183 | parent.stub(:new){ instance } 184 | instance.should_receive(:public_send).with('first=', 'first') 185 | parent.sham!.should == instance 186 | end 187 | 188 | it 'configures attribute shams' do 189 | attributes = { :first => 'first', :last => 'last' } 190 | Sham.config(parent) do |c| 191 | c.attributes{ attributes } 192 | end 193 | 194 | parent.should_receive(:new).with(attributes) 195 | parent.sham! 196 | end 197 | 198 | it 'configures empty shams' do 199 | Sham.config(parent){ |c| c.empty } 200 | parent.should_receive(:new).with({}) 201 | parent.sham! 202 | end 203 | 204 | it 'configures no arg shams' do 205 | Sham.config(parent){ |c| c.no_args } 206 | parent.should_receive(:new).with(no_args) 207 | parent.sham! 208 | end 209 | 210 | it 'configures parameter shams' do 211 | parameters = [:first, :second] 212 | Sham.config(parent) do |c| 213 | c.parameters{ parameters } 214 | end 215 | 216 | parent.should_receive(:new).with(*parameters) 217 | parent.sham! 218 | end 219 | end 220 | -------------------------------------------------------------------------------- /spec/lib/sham/data_spec.rb: -------------------------------------------------------------------------------- 1 | require 'sham/data' 2 | 3 | describe Sham do 4 | context '#string!' do 5 | it 'should be a string' do 6 | described_class.string!.should be_an_instance_of(String) 7 | end 8 | 9 | it 'should have a length of 10' do 10 | described_class.string!.length.should == 10 11 | end 12 | 13 | it 'should allow different lengths' do 14 | described_class.string!(10).length.should == 20 15 | end 16 | end 17 | 18 | context '#integer!' do 19 | it 'should be an integer' do 20 | described_class.integer!.should \ 21 | be_an_instance_of(Fixnum) 22 | end 23 | 24 | it 'should be between 0 and 100' do 25 | 10.times do 26 | described_class.integer!.should satisfy{ |x| x >= 0 && x < 100 } 27 | end 28 | end 29 | 30 | it 'should allow a differnet top' do 31 | 10.times do 32 | described_class.integer!(10).should satisfy{ |x| x >= 0 && x < 10 } 33 | end 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /spec/lib/sham/lazy_spec.rb: -------------------------------------------------------------------------------- 1 | require 'sham/lazy' 2 | 3 | describe Sham::Lazy do 4 | it 'makes a lazy sham' do 5 | expect(Sham::Lazy.new.is_a?(Sham::Lazy)).to be(true) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/lib/sham/nested_spec.rb: -------------------------------------------------------------------------------- 1 | require 'sham/nested' 2 | 3 | describe Sham::Nested do 4 | it 'is a sham base' do 5 | expect(Sham::Nested.new(double).is_a?(Sham::Base)).to be(true) 6 | end 7 | 8 | it 'makes a sham base a sham nested' do 9 | expect(Sham::Base.new(double).is_a?(Sham::Nested)).to be(true) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /spec/lib/sham/util_spec.rb: -------------------------------------------------------------------------------- 1 | require 'sham/util' 2 | 3 | describe Sham::Util do 4 | context '#extract_options!' do 5 | let(:ary){ [1, 2, 3, 4, {:opt => 'arg', :opt2 => 'arg'}] } 6 | 7 | it 'should alter the original array' do 8 | expect { 9 | described_class.extract_options!(ary) 10 | }.to change{ ary.count }.from(5).to(4) 11 | end 12 | 13 | it 'should return the hash' do 14 | described_class.extract_options!(ary).keys.should include(:opt, :opt2) 15 | end 16 | 17 | it 'should succeed when there are no options to extract' do 18 | described_class.extract_options!([1,2,3]).should == {} 19 | end 20 | 21 | it 'should succeed when the array is empty' do 22 | described_class.extract_options!([]).should == {} 23 | end 24 | 25 | it 'should fail if the argument is not an array' do 26 | expect { 27 | described_class.extract_options!(10) 28 | }.to raise_error(NoMethodError) 29 | end 30 | end 31 | 32 | context '#constantize' do 33 | before do 34 | Object.send(:remove_const, :User) if defined?(User) 35 | class User; end 36 | end 37 | 38 | it 'should raise a NameError when the constant is not valid' do 39 | expect { 40 | described_class.constantize('user') 41 | }.to raise_error(NameError) 42 | end 43 | 44 | it 'should raise a NameError when the constant is undefined' do 45 | expect { 46 | described_class.constantize("U#{Sham.string!}") 47 | }.to raise_error(NameError) 48 | end 49 | 50 | it 'should return the constant' do 51 | described_class.constantize('User').should == User 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /spec/root/sham/employee.rb: -------------------------------------------------------------------------------- 1 | class Employee; end 2 | 3 | Sham.config(Employee){ |c| c.empty } 4 | --------------------------------------------------------------------------------