socialcast_shoulda_ext 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/Gemfile +13 -0
- data/MIT-LICENSE +22 -0
- data/README.rdoc +126 -0
- data/Rakefile +12 -0
- data/lib/shoulda_ext.rb +7 -0
- data/lib/shoulda_ext/assertions.rb +14 -0
- data/lib/shoulda_ext/integrations/test_unit.rb +13 -0
- data/lib/shoulda_ext/matchers.rb +9 -0
- data/lib/shoulda_ext/matchers/record_count_change.rb +127 -0
- data/lib/shoulda_ext/matchers/respond_with_json.rb +99 -0
- data/lib/shoulda_ext/matchers/trigger_callback.rb +131 -0
- data/lib/shoulda_ext/shoulda_patches/context_with_matcher_before_hooks.rb +32 -0
- data/lib/shoulda_ext/version.rb +4 -0
- data/socialcast_shoulda_ext.gemspec +22 -0
- data/test/helper.rb +30 -0
- data/test/rails3_root/.gitignore +4 -0
- data/test/rails3_root/Rakefile +7 -0
- data/test/rails3_root/app/controllers/application_controller.rb +3 -0
- data/test/rails3_root/app/controllers/blogs_controller.rb +11 -0
- data/test/rails3_root/app/controllers/comments_controller.rb +2 -0
- data/test/rails3_root/app/helpers/application_helper.rb +2 -0
- data/test/rails3_root/app/helpers/blogs_helper.rb +2 -0
- data/test/rails3_root/app/helpers/comments_helper.rb +2 -0
- data/test/rails3_root/app/models/blog.rb +3 -0
- data/test/rails3_root/app/models/comment.rb +3 -0
- data/test/rails3_root/app/views/blogs/index.html.erb +6 -0
- data/test/rails3_root/app/views/layouts/application.html.erb +14 -0
- data/test/rails3_root/config.ru +4 -0
- data/test/rails3_root/config/application.rb +17 -0
- data/test/rails3_root/config/boot.rb +6 -0
- data/test/rails3_root/config/database.yml +22 -0
- data/test/rails3_root/config/environment.rb +5 -0
- data/test/rails3_root/config/environments/development.rb +26 -0
- data/test/rails3_root/config/environments/production.rb +49 -0
- data/test/rails3_root/config/environments/test.rb +35 -0
- data/test/rails3_root/config/initializers/backtrace_silencers.rb +7 -0
- data/test/rails3_root/config/initializers/inflections.rb +10 -0
- data/test/rails3_root/config/initializers/mime_types.rb +5 -0
- data/test/rails3_root/config/initializers/secret_token.rb +7 -0
- data/test/rails3_root/config/initializers/session_store.rb +8 -0
- data/test/rails3_root/config/locales/en.yml +5 -0
- data/test/rails3_root/config/routes.rb +4 -0
- data/test/rails3_root/db/migrate/20110411025326_create_blogs.rb +12 -0
- data/test/rails3_root/db/migrate/20110411025332_create_comments.rb +13 -0
- data/test/rails3_root/db/seeds.rb +7 -0
- data/test/rails3_root/lib/tasks/.gitkeep +0 -0
- data/test/rails3_root/public/404.html +26 -0
- data/test/rails3_root/public/422.html +26 -0
- data/test/rails3_root/public/500.html +26 -0
- data/test/rails3_root/public/favicon.ico +0 -0
- data/test/rails3_root/public/images/rails.png +0 -0
- data/test/rails3_root/public/index.html +239 -0
- data/test/rails3_root/public/javascripts/application.js +2 -0
- data/test/rails3_root/public/javascripts/controls.js +965 -0
- data/test/rails3_root/public/javascripts/dragdrop.js +974 -0
- data/test/rails3_root/public/javascripts/effects.js +1123 -0
- data/test/rails3_root/public/javascripts/prototype.js +6001 -0
- data/test/rails3_root/public/javascripts/rails.js +191 -0
- data/test/rails3_root/public/robots.txt +5 -0
- data/test/rails3_root/public/stylesheets/.gitkeep +0 -0
- data/test/rails3_root/script/rails +6 -0
- data/test/rails3_root/test/fixtures/blogs.yml +5 -0
- data/test/rails3_root/test/fixtures/comments.yml +0 -0
- data/test/rails3_root/test/functional/blogs_controller_test.rb +8 -0
- data/test/rails3_root/test/functional/comments_controller_test.rb +8 -0
- data/test/rails3_root/test/shoulda_ext.sqlite3 +0 -0
- data/test/rails3_root/test/test_helper.rb +13 -0
- data/test/rails3_root/test/unit/blog_test.rb +8 -0
- data/test/rails3_root/test/unit/comment_test.rb +8 -0
- data/test/rails3_root/test/unit/helpers/blogs_helper_test.rb +4 -0
- data/test/rails3_root/test/unit/helpers/comments_helper_test.rb +4 -0
- data/test/rails3_root/vendor/plugins/.gitkeep +0 -0
- data/test/test_assertions.rb +14 -0
- data/test/test_record_count_change_matcher.rb +150 -0
- data/test/test_respond_with_json.rb +42 -0
- data/test/test_trigger_callback_matcher.rb +38 -0
- metadata +362 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2011, Geoffrey Hichborn, Socialcast, Inc.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
4
|
+
obtaining a copy of this software and associated documentation
|
5
|
+
files (the "Software"), to deal in the Software without
|
6
|
+
restriction, including without limitation the rights to use,
|
7
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the
|
9
|
+
Software is furnished to do so, subject to the following
|
10
|
+
conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
19
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
20
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
|
2
|
+
= Socialcast Shoulda Extensions
|
3
|
+
Adds new matchers and functionality to the shoulda test library
|
4
|
+
|
5
|
+
= Installation
|
6
|
+
|
7
|
+
In your Gemfile:
|
8
|
+
|
9
|
+
group :test do
|
10
|
+
gem 'socialcast_shoulda_ext', :git => 'git@github.com:socialcast/socialcast-shoulda-ext.git', :require => 'shoulda_ext'
|
11
|
+
end
|
12
|
+
|
13
|
+
If you want to include the trigger_callbacks matcher, also add the following to your test helper:
|
14
|
+
|
15
|
+
ShouldaExt::Matchers::TriggerCallbackMatcher.attach_active_record_callback_hooks!
|
16
|
+
|
17
|
+
= Matchers
|
18
|
+
|
19
|
+
== RecordCountChangeMatcher
|
20
|
+
Test if the count for a model has changed, and by how much. Requires the context_with_matcher_before_hooks patch, which is included by default.
|
21
|
+
|
22
|
+
Provides the following matcher methods:
|
23
|
+
|
24
|
+
- create_record(klass_or_symbol)
|
25
|
+
Alias for change_record_count.for(klass_or_symbol).by(1)
|
26
|
+
|
27
|
+
- create_records(klass_or_symbol, amount)
|
28
|
+
Alias for change_record_count.for(klass_or_symbol).by(amount)
|
29
|
+
|
30
|
+
- destroy_record(klass_or_symbol)
|
31
|
+
Alias for change_record_count.for(klass_or_symbol).by(-1)
|
32
|
+
|
33
|
+
- destroy_records(klass_or_symbol, amount)
|
34
|
+
Alias for change_record_count.for(klass_or_symbol).by(-amount)
|
35
|
+
|
36
|
+
- change_record_count
|
37
|
+
Tests the difference in record count before and after the current setup/subject block
|
38
|
+
Can be used with the follow methods:
|
39
|
+
- for(klass_or_symbol)
|
40
|
+
Provides the class which the test is being performed on. Can be a constant or a symbol
|
41
|
+
|
42
|
+
- by(amount)
|
43
|
+
Provides an expected difference for the number of records for the specified class.
|
44
|
+
Excluding this number will allow the matcher to check for any difference
|
45
|
+
|
46
|
+
Examples:
|
47
|
+
|
48
|
+
context "creating a blog article" do
|
49
|
+
|
50
|
+
context "with good parameters" do
|
51
|
+
setup do
|
52
|
+
post :create, :blog => {:title => 'my blog post', :body => 'Ipsum lorem...'}
|
53
|
+
end
|
54
|
+
should create_record :blog
|
55
|
+
end
|
56
|
+
|
57
|
+
context "without a body" do
|
58
|
+
setup do
|
59
|
+
post :create, :blog => {:title => 'my blog post' }
|
60
|
+
end
|
61
|
+
should_not create_record Blog
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
== RespondWithJson
|
67
|
+
Check if the controller's response is json
|
68
|
+
|
69
|
+
Examples:
|
70
|
+
|
71
|
+
context ":index.json" do
|
72
|
+
setup do
|
73
|
+
get :index, :format => 'json'
|
74
|
+
end
|
75
|
+
# Just check to see that the response was json
|
76
|
+
should respond_with_json
|
77
|
+
|
78
|
+
# Evaluate the hash produced by the json yourself
|
79
|
+
should respond_with_json { |json| json.first['blog']['title'] == 'blog post 1'}
|
80
|
+
|
81
|
+
# Provide an exact match
|
82
|
+
should respond_with_json.exactly(['blog' => {'id' => 1, 'title' => 'blog post 1'}])
|
83
|
+
|
84
|
+
# Provide an exact match with a block
|
85
|
+
should response_with_json.exactly{ |json| JSON.parse(Blog.all.to_json)}
|
86
|
+
end
|
87
|
+
|
88
|
+
context ":index.html" do
|
89
|
+
setup do
|
90
|
+
get :index
|
91
|
+
end
|
92
|
+
|
93
|
+
# or the negation
|
94
|
+
should_not respond_with_json
|
95
|
+
end
|
96
|
+
|
97
|
+
== TriggerCallbackMatcher
|
98
|
+
|
99
|
+
Test if create, update, destroy, or save callbacks were triggered.
|
100
|
+
|
101
|
+
Requires running
|
102
|
+
ShouldaExt::Matchers::TriggerCallbackMatcher.attach_active_record_callback_hooks!
|
103
|
+
in your test suite in order to work properly.
|
104
|
+
|
105
|
+
Examples:
|
106
|
+
|
107
|
+
context "doing nothing to a record" do
|
108
|
+
subject { Blog.new :title => 'blog title' }
|
109
|
+
should_not trigger_callbacks
|
110
|
+
end
|
111
|
+
|
112
|
+
context "creating a record" do
|
113
|
+
subject { Blog.create! :title => 'blog title' }
|
114
|
+
should trigger_callbacks.for :create
|
115
|
+
should_not trigger_callbacks.for :update, :destroy
|
116
|
+
end
|
117
|
+
|
118
|
+
= Integrations
|
119
|
+
|
120
|
+
Currently only integrates with test/unit. RSpec support to come.
|
121
|
+
|
122
|
+
= Shoulda Extensions
|
123
|
+
|
124
|
+
== ContextWithMatcherBeforeHooks
|
125
|
+
|
126
|
+
Adds the ability to define a 'before' method on any method which will be run before each context's setup/subject block. Used by RecordCountChangeMatcher to record the number of records before the tested operation takes place.
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
require 'rake'
|
5
|
+
|
6
|
+
require 'rake/testtask'
|
7
|
+
Rake::TestTask.new(:test) do |test|
|
8
|
+
test.libs << 'lib' << 'test'
|
9
|
+
test.pattern = 'test/**/test_*.rb'
|
10
|
+
test.verbose = true
|
11
|
+
end
|
12
|
+
task :default => :test
|
data/lib/shoulda_ext.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
|
2
|
+
module ShouldaExt
|
3
|
+
module Assertions
|
4
|
+
# Asserts the result of object.blank?
|
5
|
+
def assert_blank(object, message = nil)
|
6
|
+
assert(object.blank?, message || "Expected object to be blank")
|
7
|
+
end
|
8
|
+
|
9
|
+
# Asserts the result of !object.blank?
|
10
|
+
def assert_not_blank(object, message = nil)
|
11
|
+
assert(!object.blank?, message || "Expected object to not be blank")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
|
2
|
+
require 'shoulda_ext/shoulda_patches/context_with_matcher_before_hooks'
|
3
|
+
|
4
|
+
module ShouldaExt # :nodoc:
|
5
|
+
module Matchers # :nodoc:
|
6
|
+
|
7
|
+
# Test if a record of klass is created during the current setup/subject block
|
8
|
+
# Equivilent to change_record_count.for(klass).by(1)
|
9
|
+
def create_record(klass)
|
10
|
+
RecordCountChangeMatcher.new.for(klass).by(1)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Test if a record of klass is destroyed during the current setup/subject block
|
14
|
+
# Equivilent to change_record_count.for(klass).by(-1)
|
15
|
+
def destroy_record(klass)
|
16
|
+
RecordCountChangeMatcher.new.for(klass).by(-1)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Test if 'count' records of 'klass' are created during the current setup/subject block
|
20
|
+
# Equivilent to change_record_count.for(klass).by(count)
|
21
|
+
def create_records(klass, count)
|
22
|
+
RecordCountChangeMatcher.new.for(klass).by(count)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Test if 'count' records of 'klass' are destroyed during the current setup/subject block
|
26
|
+
# Equivilent to change_record_count.for(klass).by(-count)
|
27
|
+
def destroy_records(klass, count)
|
28
|
+
RecordCountChangeMatcher.new.for(klass).by(-count)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Tests the difference in record count before and after the current setup/subject block
|
32
|
+
# Can be used with the follow methods:
|
33
|
+
# - for(klass)
|
34
|
+
# Provides the class which the test is being performed on. Can be a constant or a symbol
|
35
|
+
#
|
36
|
+
# - by(count)
|
37
|
+
# Provides an expected difference for the number of records for the specified class. If
|
38
|
+
# this count is not specified, the matcher will test for any difference.
|
39
|
+
#
|
40
|
+
# Examples:
|
41
|
+
#
|
42
|
+
# context "creating a blog article" do
|
43
|
+
#
|
44
|
+
# context "with good parameters" do
|
45
|
+
# setup do
|
46
|
+
# post :create, :blog => {:title => 'my blog post', :body => 'Ipsum lorem...'}
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# # All of the below are synonomous
|
50
|
+
# should create_record Blog
|
51
|
+
# should create_record :blog
|
52
|
+
# should change_record_count.for(Blog).by(1)
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# context "without a body" do
|
56
|
+
# setup do
|
57
|
+
# post :create, :blog => {:title => 'my blog post' }
|
58
|
+
# end
|
59
|
+
#
|
60
|
+
# # All of the below are synonomous
|
61
|
+
# should_not create_record Blog
|
62
|
+
# should_not change_record_count.for(Blog)
|
63
|
+
# end
|
64
|
+
#
|
65
|
+
# end
|
66
|
+
def change_record_count
|
67
|
+
RecordCountChangeMatcher.new
|
68
|
+
end
|
69
|
+
|
70
|
+
class RecordCountChangeMatcher # :nodoc:
|
71
|
+
|
72
|
+
def before
|
73
|
+
@triggered_before = true
|
74
|
+
@previous_count = @klass.count
|
75
|
+
end
|
76
|
+
|
77
|
+
def for(klass)
|
78
|
+
klass = klass.to_s.classify.constantize if klass.is_a? Symbol
|
79
|
+
@klass = klass
|
80
|
+
self
|
81
|
+
end
|
82
|
+
|
83
|
+
def by(expected_change)
|
84
|
+
@expected_change = expected_change
|
85
|
+
self
|
86
|
+
end
|
87
|
+
|
88
|
+
def matches?(*)
|
89
|
+
@new_count = @klass.count
|
90
|
+
found_expected?
|
91
|
+
end
|
92
|
+
|
93
|
+
def description
|
94
|
+
"expect #{@klass.name}.count to change#{" by #{@expected_change} records" if @expected_change }"
|
95
|
+
end
|
96
|
+
|
97
|
+
def failure_message
|
98
|
+
"Expected #{errors.join("\n")}"
|
99
|
+
end
|
100
|
+
|
101
|
+
def negative_failure_message
|
102
|
+
"Did not expect #{errors.join("\n")}"
|
103
|
+
end
|
104
|
+
|
105
|
+
def errors
|
106
|
+
@errors = []
|
107
|
+
@errors << "Never received :before call. Please make sure you are running this with a setup/subject block in the current context and the ContextWithMatcherBeforeHooks patch is installed" if !@triggered_before
|
108
|
+
@errors << "#{expected_count} #{@klass.name} records, but found #{@new_count}" if !found_expected? && @expected_change
|
109
|
+
@errors << "#{@klass.name}.count to change" if !found_expected? && !@expected_change
|
110
|
+
@errors
|
111
|
+
end
|
112
|
+
|
113
|
+
def expected_count
|
114
|
+
@previous_count + @expected_change
|
115
|
+
end
|
116
|
+
|
117
|
+
def found_expected?
|
118
|
+
if @expected_change
|
119
|
+
expected_count == @new_count
|
120
|
+
else
|
121
|
+
@new_count != @previous_count
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module ShouldaExt # :nodoc:
|
2
|
+
module Matchers # :nodoc:
|
3
|
+
|
4
|
+
# Check if the controller's response is json
|
5
|
+
#
|
6
|
+
# Example uses:
|
7
|
+
# context ":index.json" do
|
8
|
+
# setup do
|
9
|
+
# get :index, :format => 'json'
|
10
|
+
# end
|
11
|
+
# # Just check to see that the response was json
|
12
|
+
# should respond_with_json
|
13
|
+
#
|
14
|
+
# # Evaluate the hash produced by the json yourself
|
15
|
+
# should respond_with_json { |json| json.first['blog']['title'] == 'blog post 1'}
|
16
|
+
#
|
17
|
+
# # Provide an exact match
|
18
|
+
# should respond_with_json.exactly(['blog' => {'id' => 1, 'title' => 'blog post 1'}])
|
19
|
+
#
|
20
|
+
# # Provide an exact match with a block
|
21
|
+
# should response_with_json.exactly{ |json| JSON.parse(Blog.all.to_json)}
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# context ":index.html" do
|
25
|
+
# setup do
|
26
|
+
# get :index
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# # or the negation
|
30
|
+
# should_not respond_with_json
|
31
|
+
# end
|
32
|
+
def respond_with_json(description = nil, &block)
|
33
|
+
RespondWithJsonMatcher.new(self, description, &block)
|
34
|
+
end
|
35
|
+
|
36
|
+
class RespondWithJsonMatcher
|
37
|
+
def initialize(context, description = nil, &block)
|
38
|
+
@block = block || lambda{|*| true }
|
39
|
+
@description = description
|
40
|
+
@exactly = false
|
41
|
+
end
|
42
|
+
|
43
|
+
# Provide an exact result directly or using a block argument
|
44
|
+
def exactly(expected_json = nil, &block)
|
45
|
+
@exactly = true
|
46
|
+
@expected_value = expected_json
|
47
|
+
@block = block
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
def in_context(context)
|
52
|
+
@context = context
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
def description
|
57
|
+
@description ||= "respond with JSON"
|
58
|
+
end
|
59
|
+
|
60
|
+
def failure_message
|
61
|
+
"Expected to respond with json, but: #{errors.join("\n")}"
|
62
|
+
end
|
63
|
+
|
64
|
+
def negative_failure_message
|
65
|
+
"Expected not to respond with json, but: #{errors.join("\n")}"
|
66
|
+
end
|
67
|
+
|
68
|
+
def errors
|
69
|
+
@errors = []
|
70
|
+
@errors << 'Failed to parse the response as JSON' unless @parsable
|
71
|
+
@errors << "Failed to #{'exactly ' if @exactly}match json: expected #{@exactly ? @expected_value.inspect : true}, but found #{@exactly ? @response_json.inspect : @block_result} " unless @json_matched
|
72
|
+
@errors
|
73
|
+
end
|
74
|
+
|
75
|
+
def json_matched?
|
76
|
+
@expected_value ||= run_block
|
77
|
+
@json_matched ||= @exactly ? @response_json == @expected_value : !!@block_result
|
78
|
+
end
|
79
|
+
|
80
|
+
def run_block
|
81
|
+
@block_result = @context.instance_exec(@response_json, &@block)
|
82
|
+
end
|
83
|
+
|
84
|
+
def parsable?
|
85
|
+
@parsable = true
|
86
|
+
@response_json = JSON.parse(@subject.response.body)
|
87
|
+
rescue
|
88
|
+
@parsable = false
|
89
|
+
end
|
90
|
+
|
91
|
+
def matches?(subject)
|
92
|
+
@subject = subject
|
93
|
+
parsable? and json_matched?
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|