dchelimsky / rspec-rails
- Source
- Commits
- Network (68)
- Downloads (36)
- Wiki (1)
- Graphs
-
Tree:
3c59e0c
David Chelimsky (committer)
Mon Aug 11 04:37:59 -0700 2008
| ce49b6c7 » | dchelimsky | 2008-04-28 | 1 | module Spec | |
| 2 | module Rails | ||||
| 3 | |||||
| 4 | class IllegalDataAccessException < StandardError; end | ||||
| 5 | |||||
| 6 | module Mocks | ||||
| 7 | |||||
| 8 | # Creates a mock object instance for a +model_class+ with common | ||||
| 9 | # methods stubbed out. Additional methods may be easily stubbed (via | ||||
| 10 | # add_stubs) if +stubs+ is passed. | ||||
| 11 | def mock_model(model_class, options_and_stubs = {}) | ||||
| 9a1e10e7 » | adzap | 2008-06-24 | 12 | id = options_and_stubs[:id] || next_id | |
| ce49b6c7 » | dchelimsky | 2008-04-28 | 13 | options_and_stubs.reverse_merge!({ | |
| 14 | :id => id, | ||||
| 15 | :to_param => id.to_s, | ||||
| 16 | :new_record? => false, | ||||
| 17 | :errors => stub("errors", :count => 0) | ||||
| 18 | }) | ||||
| 9a1e10e7 » | adzap | 2008-06-24 | 19 | m = mock("#{model_class.name}_#{id}", options_and_stubs) | |
| ce49b6c7 » | dchelimsky | 2008-04-28 | 20 | m.send(:__mock_proxy).instance_eval <<-CODE | |
| 3c59e0c7 » | zdennis | 2008-08-07 | 21 | def @target.as_new_record | |
| 22 | self.stub!(:id).and_return nil | ||||
| 23 | self.stub!(:to_param).and_return nil | ||||
| 24 | self.stub!(:new_record?).and_return true | ||||
| 25 | self | ||||
| 26 | end | ||||
| ce49b6c7 » | dchelimsky | 2008-04-28 | 27 | def @target.is_a?(other) | |
| 28 | #{model_class}.ancestors.include?(other) | ||||
| 29 | end | ||||
| 30 | def @target.kind_of?(other) | ||||
| 31 | #{model_class}.ancestors.include?(other) | ||||
| 32 | end | ||||
| 33 | def @target.instance_of?(other) | ||||
| 34 | other == #{model_class} | ||||
| 35 | end | ||||
| 36 | def @target.class | ||||
| 37 | #{model_class} | ||||
| 38 | end | ||||
| 39 | CODE | ||||
| 40 | yield m if block_given? | ||||
| 41 | m | ||||
| 42 | end | ||||
| 38542031 » | dchelimsky | 2008-06-09 | 43 | ||
| 44 | module ModelStubber | ||||
| 45 | def connection | ||||
| 46 | raise Spec::Rails::IllegalDataAccessException.new("stubbed models are not allowed to access the database") | ||||
| 47 | end | ||||
| 48 | def new_record? | ||||
| 49 | id.nil? | ||||
| 50 | end | ||||
| 51 | def as_new_record | ||||
| 52 | self.id = nil | ||||
| 53 | self | ||||
| 54 | end | ||||
| 55 | end | ||||
| ce49b6c7 » | dchelimsky | 2008-04-28 | 56 | ||
| 57 | # :call-seq: | ||||
| 58 | # stub_model(Model) | ||||
| 59 | # stub_model(Model).as_new_record | ||||
| 60 | # stub_model(Model, hash_of_stubs) | ||||
| 344fc0cc » | dchelimsky | 2008-06-29 | 61 | # stub_model(Model, instance_variable_name, hash_of_stubs) | |
| ce49b6c7 » | dchelimsky | 2008-04-28 | 62 | # | |
| 63 | # Creates an instance of +Model+ that is prohibited from accessing the | ||||
| 344fc0cc » | dchelimsky | 2008-06-29 | 64 | # database*. For each key in +hash_of_stubs+, if the model has a | |
| 65 | # matching attribute (determined by asking it) are simply assigned the | ||||
| 66 | # submitted values. If the model does not have a matching attribute, the | ||||
| 67 | # key/value pair is assigned as a stub return value using RSpec's | ||||
| 68 | # mocking/stubbing framework. | ||||
| ce49b6c7 » | dchelimsky | 2008-04-28 | 69 | # | |
| 344fc0cc » | dchelimsky | 2008-06-29 | 70 | # <tt>new_record?</tt> is overridden to return the result of id.nil? | |
| 71 | # This means that by default new_record? will return false. If you want | ||||
| 72 | # the object to behave as a new record, sending it +as_new_record+ will | ||||
| ce49b6c7 » | dchelimsky | 2008-04-28 | 73 | # set the id to nil. You can also explicitly set :id => nil, in which | |
| 344fc0cc » | dchelimsky | 2008-06-29 | 74 | # case new_record? will return true, but using +as_new_record+ makes the | |
| 75 | # example a bit more descriptive. | ||||
| ce49b6c7 » | dchelimsky | 2008-04-28 | 76 | # | |
| 344fc0cc » | dchelimsky | 2008-06-29 | 77 | # While you can use stub_model in any example (model, view, controller, | |
| 78 | # helper), it is especially useful in view examples, which are | ||||
| 79 | # inherently more state-based than interaction-based. | ||||
| 80 | # | ||||
| 81 | # == Database Independence | ||||
| 82 | # | ||||
| 83 | # +stub_model+ does not make your examples entirely | ||||
| 84 | # database-independent. It does not stop the model class itself from | ||||
| 85 | # loading up its columns from the database. It just prevents data access | ||||
| 86 | # from the object itself. To completely decouple from the database, take | ||||
| 87 | # a look at libraries like unit_record or NullDB. | ||||
| ce49b6c7 » | dchelimsky | 2008-04-28 | 88 | # | |
| 89 | # == Examples | ||||
| 90 | # | ||||
| 91 | # stub_model(Person) | ||||
| 92 | # stub_model(Person).as_new_record | ||||
| 93 | # stub_model(Person, :id => 37) | ||||
| 94 | # stub_model(Person) do |person| | ||||
| 344fc0cc » | dchelimsky | 2008-06-29 | 95 | # person.first_name = "David" | |
| ce49b6c7 » | dchelimsky | 2008-04-28 | 96 | # end | |
| 344fc0cc » | dchelimsky | 2008-06-29 | 97 | def stub_model(model_class, stubs={}) | |
| ce49b6c7 » | dchelimsky | 2008-04-28 | 98 | stubs = {:id => next_id}.merge(stubs) | |
| 99 | returning model_class.new do |model| | ||||
| 100 | model.id = stubs.delete(:id) | ||||
| 38542031 » | dchelimsky | 2008-06-09 | 101 | model.extend ModelStubber | |
| ce49b6c7 » | dchelimsky | 2008-04-28 | 102 | stubs.each do |k,v| | |
| 103 | if model.has_attribute?(k) | ||||
| 104 | model[k] = stubs.delete(k) | ||||
| 105 | end | ||||
| 106 | end | ||||
| 107 | add_stubs(model, stubs) | ||||
| 108 | yield model if block_given? | ||||
| 109 | end | ||||
| 110 | end | ||||
| 111 | |||||
| 112 | #-- | ||||
| 113 | # TODO - Shouldn't this just be an extension of stub! ?? | ||||
| 114 | # - object.stub!(:method => return_value, :method2 => return_value2, :etc => etc) | ||||
| 115 | #++ | ||||
| 116 | # Stubs methods on +object+ (if +object+ is a symbol or string a new mock | ||||
| 344fc0cc » | dchelimsky | 2008-06-29 | 117 | # with that name will be created). +stubs+ is a Hash of +method=>value+ | |
| ce49b6c7 » | dchelimsky | 2008-04-28 | 118 | def add_stubs(object, stubs = {}) #:nodoc: | |
| 119 | m = [String, Symbol].index(object.class) ? mock(object.to_s) : object | ||||
| 120 | stubs.each {|k,v| m.stub!(k).and_return(v)} | ||||
| 121 | m | ||||
| 122 | end | ||||
| 123 | |||||
| 124 | private | ||||
| 125 | @@model_id = 1000 | ||||
| 126 | def next_id | ||||
| 127 | @@model_id += 1 | ||||
| 128 | end | ||||
| 129 | |||||
| 130 | end | ||||
| 131 | end | ||||
| 9a1e10e7 » | adzap | 2008-06-24 | 132 | end | |
