rspec-rails 1.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +57 -0
- data/Manifest.txt +158 -0
- data/README.txt +81 -0
- data/Rakefile +39 -0
- data/UPGRADE +7 -0
- data/generators/rspec/CHANGES +1 -0
- data/generators/rspec/rspec_generator.rb +40 -0
- data/generators/rspec/templates/all_stories.rb +4 -0
- data/generators/rspec/templates/previous_failures.txt +0 -0
- data/generators/rspec/templates/rcov.opts +2 -0
- data/generators/rspec/templates/rspec.rake +132 -0
- data/generators/rspec/templates/script/autospec +3 -0
- data/generators/rspec/templates/script/spec +4 -0
- data/generators/rspec/templates/script/spec_server +116 -0
- data/generators/rspec/templates/spec.opts +4 -0
- data/generators/rspec/templates/spec_helper.rb +47 -0
- data/generators/rspec/templates/stories_helper.rb +3 -0
- data/generators/rspec_controller/USAGE +33 -0
- data/generators/rspec_controller/rspec_controller_generator.rb +49 -0
- data/generators/rspec_controller/templates/controller_spec.rb +25 -0
- data/generators/rspec_controller/templates/helper_spec.rb +11 -0
- data/generators/rspec_controller/templates/view_spec.rb +12 -0
- data/generators/rspec_default_values.rb +19 -0
- data/generators/rspec_model/USAGE +18 -0
- data/generators/rspec_model/rspec_model_generator.rb +35 -0
- data/generators/rspec_model/templates/model_spec.rb +15 -0
- data/generators/rspec_scaffold/rspec_scaffold_generator.rb +154 -0
- data/generators/rspec_scaffold/templates/controller_spec.rb +173 -0
- data/generators/rspec_scaffold/templates/edit_erb_spec.rb +26 -0
- data/generators/rspec_scaffold/templates/helper_spec.rb +11 -0
- data/generators/rspec_scaffold/templates/index_erb_spec.rb +27 -0
- data/generators/rspec_scaffold/templates/new_erb_spec.rb +26 -0
- data/generators/rspec_scaffold/templates/routing_spec.rb +59 -0
- data/generators/rspec_scaffold/templates/show_erb_spec.rb +23 -0
- data/init.rb +9 -0
- data/lib/autotest/discover.rb +1 -0
- data/lib/autotest/rails_rspec.rb +76 -0
- data/lib/spec/rails.rb +15 -0
- data/lib/spec/rails/example.rb +47 -0
- data/lib/spec/rails/example/assigns_hash_proxy.rb +43 -0
- data/lib/spec/rails/example/controller_example_group.rb +256 -0
- data/lib/spec/rails/example/cookies_proxy.rb +25 -0
- data/lib/spec/rails/example/functional_example_group.rb +87 -0
- data/lib/spec/rails/example/helper_example_group.rb +166 -0
- data/lib/spec/rails/example/model_example_group.rb +14 -0
- data/lib/spec/rails/example/rails_example_group.rb +33 -0
- data/lib/spec/rails/example/render_observer.rb +93 -0
- data/lib/spec/rails/example/view_example_group.rb +183 -0
- data/lib/spec/rails/extensions.rb +12 -0
- data/lib/spec/rails/extensions/action_controller/base.rb +14 -0
- data/lib/spec/rails/extensions/action_controller/rescue.rb +21 -0
- data/lib/spec/rails/extensions/action_controller/test_response.rb +11 -0
- data/lib/spec/rails/extensions/action_view/base.rb +31 -0
- data/lib/spec/rails/extensions/active_record/base.rb +30 -0
- data/lib/spec/rails/extensions/object.rb +5 -0
- data/lib/spec/rails/extensions/spec/example/configuration.rb +71 -0
- data/lib/spec/rails/extensions/spec/matchers/have.rb +21 -0
- data/lib/spec/rails/interop/testcase.rb +14 -0
- data/lib/spec/rails/matchers.rb +31 -0
- data/lib/spec/rails/matchers/assert_select.rb +131 -0
- data/lib/spec/rails/matchers/change.rb +11 -0
- data/lib/spec/rails/matchers/have_text.rb +57 -0
- data/lib/spec/rails/matchers/include_text.rb +54 -0
- data/lib/spec/rails/matchers/redirect_to.rb +113 -0
- data/lib/spec/rails/matchers/render_template.rb +90 -0
- data/lib/spec/rails/mocks.rb +132 -0
- data/lib/spec/rails/story_adapter.rb +79 -0
- data/lib/spec/rails/version.rb +15 -0
- data/spec/rails/autotest/mappings_spec.rb +36 -0
- data/spec/rails/example/assigns_hash_proxy_spec.rb +65 -0
- data/spec/rails/example/configuration_spec.rb +83 -0
- data/spec/rails/example/controller_isolation_spec.rb +62 -0
- data/spec/rails/example/controller_spec_spec.rb +272 -0
- data/spec/rails/example/cookies_proxy_spec.rb +74 -0
- data/spec/rails/example/example_group_factory_spec.rb +112 -0
- data/spec/rails/example/helper_spec_spec.rb +161 -0
- data/spec/rails/example/model_spec_spec.rb +18 -0
- data/spec/rails/example/shared_behaviour_spec.rb +16 -0
- data/spec/rails/example/test_unit_assertion_accessibility_spec.rb +33 -0
- data/spec/rails/example/view_spec_spec.rb +280 -0
- data/spec/rails/extensions/action_controller_rescue_action_spec.rb +54 -0
- data/spec/rails/extensions/action_view_base_spec.rb +48 -0
- data/spec/rails/extensions/active_record_spec.rb +14 -0
- data/spec/rails/interop/testcase_spec.rb +66 -0
- data/spec/rails/matchers/assert_select_spec.rb +814 -0
- data/spec/rails/matchers/description_generation_spec.rb +37 -0
- data/spec/rails/matchers/errors_on_spec.rb +13 -0
- data/spec/rails/matchers/have_text_spec.rb +62 -0
- data/spec/rails/matchers/include_text_spec.rb +64 -0
- data/spec/rails/matchers/redirect_to_spec.rb +209 -0
- data/spec/rails/matchers/render_template_spec.rb +176 -0
- data/spec/rails/matchers/should_change_spec.rb +15 -0
- data/spec/rails/mocks/ar_classes.rb +10 -0
- data/spec/rails/mocks/mock_model_spec.rb +106 -0
- data/spec/rails/mocks/stub_model_spec.rb +80 -0
- data/spec/rails/sample_modified_fixture.rb +8 -0
- data/spec/rails/sample_spec.rb +8 -0
- data/spec/rails/spec_server_spec.rb +96 -0
- data/spec/rails/spec_spec.rb +11 -0
- data/spec/rails_suite.rb +7 -0
- data/spec/spec_helper.rb +57 -0
- data/spec_resources/controllers/action_view_base_spec_controller.rb +2 -0
- data/spec_resources/controllers/controller_spec_controller.rb +94 -0
- data/spec_resources/controllers/redirect_spec_controller.rb +59 -0
- data/spec_resources/controllers/render_spec_controller.rb +30 -0
- data/spec_resources/controllers/rjs_spec_controller.rb +58 -0
- data/spec_resources/helpers/explicit_helper.rb +38 -0
- data/spec_resources/helpers/more_explicit_helper.rb +5 -0
- data/spec_resources/helpers/plugin_application_helper.rb +6 -0
- data/spec_resources/helpers/view_spec_helper.rb +13 -0
- data/spec_resources/views/controller_spec/_partial.rhtml +0 -0
- data/spec_resources/views/controller_spec/action_setting_flash_after_session_reset.rhtml +1 -0
- data/spec_resources/views/controller_spec/action_setting_flash_before_session_reset.rhtml +1 -0
- data/spec_resources/views/controller_spec/action_setting_the_assigns_hash.rhtml +0 -0
- data/spec_resources/views/controller_spec/action_with_errors_in_template.rhtml +1 -0
- data/spec_resources/views/controller_spec/action_with_template.rhtml +1 -0
- data/spec_resources/views/layouts/application.rhtml +0 -0
- data/spec_resources/views/layouts/simple.rhtml +0 -0
- data/spec_resources/views/objects/_object.html.erb +1 -0
- data/spec_resources/views/render_spec/_a_partial.rhtml +0 -0
- data/spec_resources/views/render_spec/action_with_alternate_layout.rhtml +0 -0
- data/spec_resources/views/render_spec/some_action.js.rjs +1 -0
- data/spec_resources/views/render_spec/some_action.rhtml +0 -0
- data/spec_resources/views/render_spec/some_action.rjs +1 -0
- data/spec_resources/views/rjs_spec/_replacement_partial.rhtml +1 -0
- data/spec_resources/views/rjs_spec/hide_div.rjs +1 -0
- data/spec_resources/views/rjs_spec/hide_page_element.rjs +1 -0
- data/spec_resources/views/rjs_spec/insert_html.rjs +1 -0
- data/spec_resources/views/rjs_spec/replace.rjs +1 -0
- data/spec_resources/views/rjs_spec/replace_html.rjs +1 -0
- data/spec_resources/views/rjs_spec/replace_html_with_partial.rjs +1 -0
- data/spec_resources/views/rjs_spec/visual_effect.rjs +1 -0
- data/spec_resources/views/rjs_spec/visual_toggle_effect.rjs +1 -0
- data/spec_resources/views/tag_spec/no_tags.rhtml +1 -0
- data/spec_resources/views/tag_spec/single_div_with_no_attributes.rhtml +1 -0
- data/spec_resources/views/tag_spec/single_div_with_one_attribute.rhtml +1 -0
- data/spec_resources/views/view_spec/_partial.rhtml +2 -0
- data/spec_resources/views/view_spec/_partial_used_twice.rhtml +0 -0
- data/spec_resources/views/view_spec/_partial_with_local_variable.rhtml +1 -0
- data/spec_resources/views/view_spec/_partial_with_sub_partial.rhtml +1 -0
- data/spec_resources/views/view_spec/_spacer.rhtml +1 -0
- data/spec_resources/views/view_spec/accessor.rhtml +5 -0
- data/spec_resources/views/view_spec/block_helper.rhtml +3 -0
- data/spec_resources/views/view_spec/entry_form.rhtml +2 -0
- data/spec_resources/views/view_spec/explicit_helper.rhtml +2 -0
- data/spec_resources/views/view_spec/foo/show.rhtml +1 -0
- data/spec_resources/views/view_spec/implicit_helper.rhtml +2 -0
- data/spec_resources/views/view_spec/multiple_helpers.rhtml +3 -0
- data/spec_resources/views/view_spec/should_not_receive.rhtml +3 -0
- data/spec_resources/views/view_spec/template_with_partial.rhtml +5 -0
- data/spec_resources/views/view_spec/template_with_partial_using_collection.rhtml +3 -0
- data/spec_resources/views/view_spec/template_with_partial_with_array.rhtml +1 -0
- data/stories/all.rb +10 -0
- data/stories/configuration/stories.rb +5 -0
- data/stories/helper.rb +6 -0
- data/stories/steps/people.rb +8 -0
- data/stories/transactions_should_rollback +15 -0
- data/stories/transactions_should_rollback.rb +25 -0
- metadata +234 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
module Spec
|
|
2
|
+
module Rails
|
|
3
|
+
module Matchers
|
|
4
|
+
|
|
5
|
+
class IncludeText #:nodoc:
|
|
6
|
+
|
|
7
|
+
def initialize(expected)
|
|
8
|
+
@expected = expected
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def matches?(response_or_text)
|
|
12
|
+
@actual = response_or_text.respond_to?(:body) ? response_or_text.body : response_or_text
|
|
13
|
+
return actual.include?(expected)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def failure_message
|
|
17
|
+
"expected to find #{expected.inspect} in #{actual.inspect}"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def negative_failure_message
|
|
21
|
+
"expected not to include text #{expected.inspect}"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def to_s
|
|
25
|
+
"include text #{expected.inspect}"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
attr_reader :expected
|
|
30
|
+
attr_reader :actual
|
|
31
|
+
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# :call-seq:
|
|
36
|
+
# response.should include_text(expected)
|
|
37
|
+
# response.should_not include_text(expected)
|
|
38
|
+
#
|
|
39
|
+
# Accepts a String, matching using include?
|
|
40
|
+
#
|
|
41
|
+
# Use this instead of <tt>response.should have_text()</tt>
|
|
42
|
+
# when you either don't know or don't care where on the page
|
|
43
|
+
# this text appears.
|
|
44
|
+
#
|
|
45
|
+
# == Examples
|
|
46
|
+
#
|
|
47
|
+
# response.should include_text("This text will be in the actual string")
|
|
48
|
+
def include_text(text)
|
|
49
|
+
IncludeText.new(text)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
module Spec
|
|
2
|
+
module Rails
|
|
3
|
+
module Matchers
|
|
4
|
+
|
|
5
|
+
class RedirectTo #:nodoc:
|
|
6
|
+
|
|
7
|
+
def initialize(request, expected)
|
|
8
|
+
@expected = expected
|
|
9
|
+
@request = request
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def matches?(response)
|
|
13
|
+
@redirected = response.redirect?
|
|
14
|
+
@actual = response.redirect_url
|
|
15
|
+
return false unless @redirected
|
|
16
|
+
if @expected.instance_of? Hash
|
|
17
|
+
return false unless @actual =~ %r{^\w+://#{@request.host}}
|
|
18
|
+
return false unless actual_redirect_to_valid_route
|
|
19
|
+
return actual_hash == expected_hash
|
|
20
|
+
else
|
|
21
|
+
return @actual == expected_url
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def actual_hash
|
|
26
|
+
hash_from_url @actual
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def expected_hash
|
|
30
|
+
hash_from_url expected_url
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def actual_redirect_to_valid_route
|
|
34
|
+
actual_hash
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def hash_from_url(url)
|
|
38
|
+
query_hash(url).merge(path_hash(url)).with_indifferent_access
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def path_hash(url)
|
|
42
|
+
path = url.sub(%r{^\w+://#{@request.host}(?::\d+)?}, "").split("?", 2)[0]
|
|
43
|
+
ActionController::Routing::Routes.recognize_path path
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def query_hash(url)
|
|
47
|
+
query = url.split("?", 2)[1] || ""
|
|
48
|
+
QueryParameterParser.parse_query_parameters(query, @request)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def expected_url
|
|
52
|
+
case @expected
|
|
53
|
+
when Hash
|
|
54
|
+
return ActionController::UrlRewriter.new(@request, {}).rewrite(@expected)
|
|
55
|
+
when :back
|
|
56
|
+
return @request.env['HTTP_REFERER']
|
|
57
|
+
when %r{^\w+://.*}
|
|
58
|
+
return @expected
|
|
59
|
+
else
|
|
60
|
+
return "http://#{@request.host}" + (@expected.split('')[0] == '/' ? '' : '/') + @expected
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def failure_message
|
|
65
|
+
if @redirected
|
|
66
|
+
return %Q{expected redirect to #{@expected.inspect}, got redirect to #{@actual.inspect}}
|
|
67
|
+
else
|
|
68
|
+
return %Q{expected redirect to #{@expected.inspect}, got no redirect}
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def negative_failure_message
|
|
73
|
+
return %Q{expected not to be redirected to #{@expected.inspect}, but was} if @redirected
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def description
|
|
77
|
+
"redirect to #{@actual.inspect}"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
class QueryParameterParser
|
|
81
|
+
def self.parse_query_parameters(query, request)
|
|
82
|
+
if defined?(CGIMethods)
|
|
83
|
+
CGIMethods.parse_query_parameters(query)
|
|
84
|
+
else
|
|
85
|
+
request.class.parse_query_parameters(query)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# :call-seq:
|
|
92
|
+
# response.should redirect_to(url)
|
|
93
|
+
# response.should redirect_to(:action => action_name)
|
|
94
|
+
# response.should redirect_to(:controller => controller_name, :action => action_name)
|
|
95
|
+
# response.should_not redirect_to(url)
|
|
96
|
+
# response.should_not redirect_to(:action => action_name)
|
|
97
|
+
# response.should_not redirect_to(:controller => controller_name, :action => action_name)
|
|
98
|
+
#
|
|
99
|
+
# Passes if the response is a redirect to the url, action or controller/action.
|
|
100
|
+
# Useful in controller specs (integration or isolation mode).
|
|
101
|
+
#
|
|
102
|
+
# == Examples
|
|
103
|
+
#
|
|
104
|
+
# response.should redirect_to("path/to/action")
|
|
105
|
+
# response.should redirect_to("http://test.host/path/to/action")
|
|
106
|
+
# response.should redirect_to(:action => 'list')
|
|
107
|
+
def redirect_to(opts)
|
|
108
|
+
RedirectTo.new(request, opts)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
module Spec
|
|
2
|
+
module Rails
|
|
3
|
+
module Matchers
|
|
4
|
+
|
|
5
|
+
class RenderTemplate #:nodoc:
|
|
6
|
+
|
|
7
|
+
def initialize(expected, controller)
|
|
8
|
+
@controller = controller
|
|
9
|
+
@expected = expected
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def matches?(response)
|
|
13
|
+
|
|
14
|
+
if response.respond_to?(:rendered_file)
|
|
15
|
+
@actual = response.rendered_file
|
|
16
|
+
else
|
|
17
|
+
@actual = response.rendered_template.to_s
|
|
18
|
+
end
|
|
19
|
+
return false if @actual.blank?
|
|
20
|
+
given_controller_path, given_file = path_and_file(@actual)
|
|
21
|
+
expected_controller_path, expected_file = path_and_file(@expected)
|
|
22
|
+
given_controller_path == expected_controller_path && given_file.match(expected_file)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def failure_message
|
|
26
|
+
"expected #{@expected.inspect}, got #{@actual.inspect}"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def negative_failure_message
|
|
30
|
+
"expected not to render #{@expected.inspect}, but did"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def description
|
|
34
|
+
"render template #{@expected.inspect}"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
def path_and_file(path)
|
|
39
|
+
parts = path.split('/')
|
|
40
|
+
file = parts.pop
|
|
41
|
+
controller = parts.empty? ? current_controller_path : parts.join('/')
|
|
42
|
+
return controller, file
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def controller_path_from(path)
|
|
46
|
+
parts = path.split('/')
|
|
47
|
+
parts.pop
|
|
48
|
+
parts.join('/')
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def current_controller_path
|
|
52
|
+
@controller.class.to_s.underscore.gsub(/_controller$/,'')
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# :call-seq:
|
|
58
|
+
# response.should render_template(path)
|
|
59
|
+
# response.should_not render_template(path)
|
|
60
|
+
#
|
|
61
|
+
# Passes if the specified template is rendered by the response.
|
|
62
|
+
# Useful in controller specs (integration or isolation mode).
|
|
63
|
+
#
|
|
64
|
+
# <code>path</code> can include the controller path or not. It
|
|
65
|
+
# can also include an optional extension (no extension assumes .rhtml).
|
|
66
|
+
#
|
|
67
|
+
# Note that partials must be spelled with the preceding underscore.
|
|
68
|
+
#
|
|
69
|
+
# == Examples
|
|
70
|
+
#
|
|
71
|
+
# response.should render_template('list')
|
|
72
|
+
# response.should render_template('same_controller/list')
|
|
73
|
+
# response.should render_template('other_controller/list')
|
|
74
|
+
#
|
|
75
|
+
# #rjs
|
|
76
|
+
# response.should render_template('list.rjs')
|
|
77
|
+
# response.should render_template('same_controller/list.rjs')
|
|
78
|
+
# response.should render_template('other_controller/list.rjs')
|
|
79
|
+
#
|
|
80
|
+
# #partials
|
|
81
|
+
# response.should render_template('_a_partial')
|
|
82
|
+
# response.should render_template('same_controller/_a_partial')
|
|
83
|
+
# response.should render_template('other_controller/_a_partial')
|
|
84
|
+
def render_template(path)
|
|
85
|
+
RenderTemplate.new(path.to_s, @controller)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,132 @@
|
|
|
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 = {})
|
|
12
|
+
id = options_and_stubs[:id] || next_id
|
|
13
|
+
options_and_stubs = 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
|
+
})
|
|
19
|
+
m = mock("#{model_class.name}_#{id}", options_and_stubs)
|
|
20
|
+
m.send(:__mock_proxy).instance_eval <<-CODE
|
|
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
|
|
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
|
|
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
|
|
56
|
+
|
|
57
|
+
# :call-seq:
|
|
58
|
+
# stub_model(Model)
|
|
59
|
+
# stub_model(Model).as_new_record
|
|
60
|
+
# stub_model(Model, hash_of_stubs)
|
|
61
|
+
# stub_model(Model, instance_variable_name, hash_of_stubs)
|
|
62
|
+
#
|
|
63
|
+
# Creates an instance of +Model+ that is prohibited from accessing the
|
|
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.
|
|
69
|
+
#
|
|
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
|
|
73
|
+
# set the id to nil. You can also explicitly set :id => nil, in which
|
|
74
|
+
# case new_record? will return true, but using +as_new_record+ makes the
|
|
75
|
+
# example a bit more descriptive.
|
|
76
|
+
#
|
|
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.
|
|
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|
|
|
95
|
+
# person.first_name = "David"
|
|
96
|
+
# end
|
|
97
|
+
def stub_model(model_class, stubs={})
|
|
98
|
+
stubs = {:id => next_id}.merge(stubs)
|
|
99
|
+
returning model_class.new do |model|
|
|
100
|
+
model.id = stubs.delete(:id)
|
|
101
|
+
model.extend ModelStubber
|
|
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
|
|
117
|
+
# with that name will be created). +stubs+ is a Hash of +method=>value+
|
|
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
|
|
132
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# WARNING - THIS IS PURELY EXPERIMENTAL AT THIS POINT
|
|
2
|
+
# Courtesy of Brian Takita and Yurii Rashkovskii
|
|
3
|
+
|
|
4
|
+
$:.unshift File.join(File.dirname(__FILE__), *%w[.. .. .. .. rspec lib])
|
|
5
|
+
if defined?(ActiveRecord::Base)
|
|
6
|
+
require 'test_help'
|
|
7
|
+
else
|
|
8
|
+
require 'action_controller/test_process'
|
|
9
|
+
require 'action_controller/integration'
|
|
10
|
+
end
|
|
11
|
+
require 'test/unit/testresult'
|
|
12
|
+
require 'spec'
|
|
13
|
+
require 'spec/rails'
|
|
14
|
+
|
|
15
|
+
Test::Unit.run = true
|
|
16
|
+
|
|
17
|
+
ActionController::Integration::Session.send(:include, Spec::Matchers)
|
|
18
|
+
ActionController::Integration::Session.send(:include, Spec::Rails::Matchers)
|
|
19
|
+
|
|
20
|
+
class RailsStory < ActionController::IntegrationTest
|
|
21
|
+
if defined?(ActiveRecord::Base)
|
|
22
|
+
self.use_transactional_fixtures = true
|
|
23
|
+
else
|
|
24
|
+
def self.fixture_table_names; []; end # Workaround for projects that don't use ActiveRecord
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def initialize #:nodoc:
|
|
28
|
+
# TODO - eliminate this hack, which is here to stop
|
|
29
|
+
# Rails Stories from dumping the example summary.
|
|
30
|
+
Spec::Runner::Options.class_eval do
|
|
31
|
+
def examples_should_be_run?
|
|
32
|
+
false
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
@_result = Test::Unit::TestResult.new
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
class ActiveRecordSafetyListener
|
|
40
|
+
include Singleton
|
|
41
|
+
def scenario_started(*args)
|
|
42
|
+
if defined?(ActiveRecord::Base)
|
|
43
|
+
if ActiveRecord::Base.connection.respond_to?(:increment_open_transactions)
|
|
44
|
+
ActiveRecord::Base.connection.increment_open_transactions
|
|
45
|
+
else
|
|
46
|
+
ActiveRecord::Base.send :increment_open_transactions
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
ActiveRecord::Base.connection.begin_db_transaction
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def scenario_succeeded(*args)
|
|
53
|
+
if defined?(ActiveRecord::Base)
|
|
54
|
+
ActiveRecord::Base.connection.rollback_db_transaction
|
|
55
|
+
if ActiveRecord::Base.connection.respond_to?(:decrement_open_transactions)
|
|
56
|
+
ActiveRecord::Base.connection.decrement_open_transactions
|
|
57
|
+
else
|
|
58
|
+
ActiveRecord::Base.send :decrement_open_transactions
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
alias :scenario_pending :scenario_succeeded
|
|
63
|
+
alias :scenario_failed :scenario_succeeded
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
class Spec::Story::Runner::ScenarioRunner
|
|
67
|
+
def initialize
|
|
68
|
+
@listeners = [ActiveRecordSafetyListener.instance]
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
class Spec::Story::GivenScenario
|
|
73
|
+
def perform(instance, name = nil)
|
|
74
|
+
scenario = Spec::Story::Runner::StoryRunner.scenario_from_current_story @name
|
|
75
|
+
runner = Spec::Story::Runner::ScenarioRunner.new
|
|
76
|
+
runner.instance_variable_set(:@listeners,[])
|
|
77
|
+
runner.run(scenario, instance)
|
|
78
|
+
end
|
|
79
|
+
end
|