active_record_block_matchers 0.1.3 → 0.2.0
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.
- checksums.yaml +4 -4
- data/.rspec +2 -1
- data/README.md +89 -10
- data/db/migrate/20151017231107_create_dogs.rb +9 -0
- data/db/schema.rb +8 -1
- data/lib/active_record_block_matchers.rb +2 -3
- data/lib/active_record_block_matchers/{create_a_matcher.rb → create_a_new_matcher.rb} +0 -0
- data/lib/active_record_block_matchers/create_records_matcher.rb +117 -0
- data/lib/active_record_block_matchers/version.rb +1 -1
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 56549819eb77b8a9e445755d2ed4d66c8a7c78d7
|
4
|
+
data.tar.gz: 5eae4e2fbe09eff5486f783ff296b437b92c84bd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a8702eb8afc69876c8b15bb624124de2aae8c0735e8c05a9f6faf67fc7cf470c3163e2332c1fee2faf6c97dec8d7a3d8695e74e0264f550eaa465e87ab210316
|
7
|
+
data.tar.gz: 4f957401eac7b5b596bf04dbe6a402f2ba0c88d12facf8e7f017c29ba1bee72b410de8834dc1f3e4c2ac9849664f761aba6b77ce88122b946320d1967965363b
|
data/.rspec
CHANGED
data/README.md
CHANGED
@@ -21,29 +21,51 @@ Or install it yourself as:
|
|
21
21
|
|
22
22
|
$ gem install active_record_block_matchers
|
23
23
|
|
24
|
-
##
|
24
|
+
## Quick Examples
|
25
25
|
|
26
|
-
|
26
|
+
```
|
27
|
+
expect {
|
28
|
+
post :create, user: { username: "bob", password: "BlueSteel45" }
|
29
|
+
}.to create_a(User)
|
30
|
+
.with_attributes(username: "bob")
|
31
|
+
.which {|bob| expect(AuthLibrary.authenticate("bob", "BlueSteel45")).to eq bob }
|
32
|
+
|
33
|
+
expect {
|
34
|
+
post :create, user: { username: "bob", password: "BlueSteel45" }
|
35
|
+
}.to create(User => 1, Profile => 1)
|
36
|
+
.with_attributes(
|
37
|
+
User => [{username: "bob"}],
|
38
|
+
Profile => [{avatar_url: Avatar.default_avatar_url}],
|
39
|
+
).which { |new_records_hash|
|
40
|
+
new_user = new_records_hash[User].first
|
41
|
+
new_profile = new_records_hash[Profile].first
|
42
|
+
expect(new_user.profile).to eq new_profile
|
43
|
+
}
|
44
|
+
```
|
45
|
+
|
46
|
+
## Detailed Examples
|
47
|
+
|
48
|
+
#### `create_a`
|
27
49
|
|
28
|
-
aliases: `
|
50
|
+
aliases: `create_an`, `create_a_new`
|
29
51
|
|
30
52
|
Example:
|
31
53
|
|
32
54
|
```ruby
|
33
|
-
expect { User.create! }.to
|
55
|
+
expect { User.create! }.to create_a(User)
|
34
56
|
```
|
35
57
|
|
36
58
|
This can be very useful for controller tests:
|
37
59
|
|
38
60
|
```ruby
|
39
|
-
expect { post :create, user: user_params }.to
|
61
|
+
expect { post :create, user: user_params }.to create_a(User)
|
40
62
|
```
|
41
63
|
|
42
64
|
You can chain `.with_attributes` as well to define a list of values you expect the new object to have. This works with both database attributes and computed values.
|
43
65
|
|
44
66
|
```ruby
|
45
67
|
expect { User.create!(username: "bob") }
|
46
|
-
.to
|
68
|
+
.to create_a(User)
|
47
69
|
.with_attributes(username: "bob")
|
48
70
|
```
|
49
71
|
|
@@ -51,7 +73,7 @@ This is a great way to test ActiveReocrd hooks on your model. For example, if y
|
|
51
73
|
|
52
74
|
```ruby
|
53
75
|
expect { User.create!(username: "BOB") }
|
54
|
-
.to
|
76
|
+
.to create_a(User)
|
55
77
|
.with_attributes(username: "bob")
|
56
78
|
```
|
57
79
|
|
@@ -59,7 +81,7 @@ You can even use RSpec's [composable matchers][1]:
|
|
59
81
|
|
60
82
|
```ruby
|
61
83
|
expect { User.create!(username: "bob") }
|
62
|
-
.to
|
84
|
+
.to create_a(User)
|
63
85
|
.with_attributes(username: a_string_starting_with("b"))
|
64
86
|
```
|
65
87
|
|
@@ -67,17 +89,74 @@ If you need to make assertions about things other than attribute equality, you c
|
|
67
89
|
|
68
90
|
```ruby
|
69
91
|
expect { User.create!(username: "BOB", password: "BlueSteel45") }
|
70
|
-
.to
|
92
|
+
.to create_a(User)
|
71
93
|
.which { |user|
|
72
94
|
expect(user.encrypted_password).to be_present
|
73
95
|
expect(AuthLibrary.authenticate("bob", "BlueSteel45")).to eq user
|
74
96
|
}
|
75
97
|
```
|
76
98
|
|
77
|
-
**Gotcha Warning:** Be careful about your block syntax when chaining `.which` in your tests. If you write the above example with a `do...end`, the example will parse like this: `expect {...}.to(
|
99
|
+
**Gotcha Warning:** Be careful about your block syntax when chaining `.which` in your tests. If you write the above example with a `do...end`, the example will parse like this: `expect {...}.to(create_a(User).which) do |user| ... end`, so your block will not execute, and it may appear that your test is passing, when it is not.
|
78
100
|
|
79
101
|
This matcher relies on a `created_at` column existing on the given model class. The name of this column can be configured via `ActiveRecordBlockMatchers::Config.created_at_column_name = "your_column_name"`
|
80
102
|
|
103
|
+
#### `create`
|
104
|
+
|
105
|
+
aliases: `create_records`
|
106
|
+
|
107
|
+
Example:
|
108
|
+
|
109
|
+
```ruby
|
110
|
+
expect { User.create!; User.create!; Profile.create! }
|
111
|
+
.to create(User => 2, Profile => 1)
|
112
|
+
```
|
113
|
+
|
114
|
+
Just like the other matcher, you can chain `with_attributes` and `which` to assert about the particulars of the records:
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
expect { UserService.sign_up!(username: "bob", password: "BlueSteel45") }
|
118
|
+
.to create(User => 1, Profile => 1)
|
119
|
+
.with_attributes(
|
120
|
+
User => [{username: "bob"}],
|
121
|
+
Profile => [{avatar_url: Avatar.default_avatar_url}]
|
122
|
+
).which { |records|
|
123
|
+
# records is a hash with model classes for keys and the new records for values
|
124
|
+
new_user = records[User].first
|
125
|
+
new_profile = records[Profile].first
|
126
|
+
expect(AuthLibrary.authenticate("bob", "BlueSteel45")).to eq new_user
|
127
|
+
expect(new_user.profile).to eq new_profile
|
128
|
+
}
|
129
|
+
```
|
130
|
+
|
131
|
+
As noted, the `which` block yields a hash containing the new records whose counts were specified.
|
132
|
+
|
133
|
+
Order doesn't matter for the attributes specified in `with_attributes`, but you must provide an attribute hash for every record that was created. This means, if you expect the block to create, say 2 User records, you must provide an attributes hash for each new User record:
|
134
|
+
|
135
|
+
```ruby
|
136
|
+
# This is correct:
|
137
|
+
expect { User.create!(username: "bob"); User.create!(username: "rhonda") }
|
138
|
+
.to create(User => 2)
|
139
|
+
.with_attributes(
|
140
|
+
User => [{username: "rhonda"}, {username: "bob"}]
|
141
|
+
)
|
142
|
+
|
143
|
+
# This will raise an error:
|
144
|
+
expect { User.create!(username: "bob"); User.create!(username: "rhonda") }
|
145
|
+
.to create(User => 2)
|
146
|
+
.with_attributes(
|
147
|
+
User => [{username: "rhonda"}]
|
148
|
+
)
|
149
|
+
|
150
|
+
# But this is totally fine if you really need a workaround:
|
151
|
+
# Just put the empty hashes last
|
152
|
+
expect { User.create!(username: "bob"); User.create!(username: "rhonda") }
|
153
|
+
.to create(User => 2)
|
154
|
+
.with_attributes(
|
155
|
+
User => [{username: "rhonda"}, {}]
|
156
|
+
)
|
157
|
+
```
|
158
|
+
|
159
|
+
|
81
160
|
## Development
|
82
161
|
|
83
162
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/db/schema.rb
CHANGED
@@ -11,7 +11,14 @@
|
|
11
11
|
#
|
12
12
|
# It's strongly recommended to check this file into your version control system.
|
13
13
|
|
14
|
-
ActiveRecord::Schema.define(:version =>
|
14
|
+
ActiveRecord::Schema.define(:version => 20151017231107) do
|
15
|
+
|
16
|
+
create_table "dogs", :force => true do |t|
|
17
|
+
t.string "name"
|
18
|
+
t.string "breed"
|
19
|
+
t.datetime "created_at", :null => false
|
20
|
+
t.datetime "updated_at", :null => false
|
21
|
+
end
|
15
22
|
|
16
23
|
create_table "people", :force => true do |t|
|
17
24
|
t.string "first_name"
|
@@ -1,8 +1,7 @@
|
|
1
|
-
require "active_record_block_matchers/version"
|
2
1
|
require "rspec/expectations"
|
3
2
|
require "active_record"
|
4
|
-
|
5
|
-
|
3
|
+
|
4
|
+
Dir[File.dirname(__FILE__) + "/active_record_block_matchers/**/*.rb"].each {|file| require file }
|
6
5
|
|
7
6
|
module ActiveRecordBlockMatchers
|
8
7
|
end
|
File without changes
|
@@ -0,0 +1,117 @@
|
|
1
|
+
RSpec::Matchers.define :create_records do |record_counts|
|
2
|
+
include ActiveSupport::Inflector
|
3
|
+
|
4
|
+
supports_block_expectations
|
5
|
+
|
6
|
+
description do
|
7
|
+
counts_strs = record_counts.map { |klass, count| count_str(klass, count) }
|
8
|
+
"create #{counts_strs.join(", ")}"
|
9
|
+
end
|
10
|
+
|
11
|
+
chain(:with_attributes) do |attributes|
|
12
|
+
if mismatch=attributes.find {|klass, hashes| hashes.size != record_counts[klass]}
|
13
|
+
mismatched_class, hashes = mismatch
|
14
|
+
raise ArgumentError, "Specified the block should create #{record_counts[mismatched_class]} #{mismatched_class}, but provided #{hashes.size} #{mismatched_class} attribute specifications"
|
15
|
+
end
|
16
|
+
@expected_attributes = attributes
|
17
|
+
end
|
18
|
+
|
19
|
+
chain(:which) do |&block|
|
20
|
+
@which_block = block
|
21
|
+
end
|
22
|
+
|
23
|
+
match do |block|
|
24
|
+
time_before = Time.current
|
25
|
+
|
26
|
+
block.call
|
27
|
+
|
28
|
+
@new_records =
|
29
|
+
record_counts.keys.each_with_object({}) do |klass, new_records|
|
30
|
+
column_name = ActiveRecordBlockMatchers::Config.created_at_column_name
|
31
|
+
new_records[klass] = klass.where("#{column_name} > ?", time_before).to_a
|
32
|
+
end
|
33
|
+
|
34
|
+
@incorrect_counts =
|
35
|
+
@new_records.each_with_object({}) do |(klass, new_records), incorrect|
|
36
|
+
actual_count = new_records.count
|
37
|
+
expected_count = record_counts[klass]
|
38
|
+
if actual_count != expected_count
|
39
|
+
incorrect[klass] = { expected: expected_count, actual: actual_count }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
return false if @incorrect_counts.any?
|
44
|
+
|
45
|
+
if @expected_attributes
|
46
|
+
@matched_records = Hash.new {|hash, key| hash[key] = []}
|
47
|
+
@all_attributes = Hash.new {|hash, key| hash[key] = []}
|
48
|
+
@incorrect_attributes =
|
49
|
+
@expected_attributes.each_with_object(Hash.new {|hash, key| hash[key] = []}) do |(klass, expected_attributes), incorrect|
|
50
|
+
@all_attributes[klass] = expected_attributes.map(&:keys).flatten.uniq
|
51
|
+
expected_attributes.each do |expected_attrs|
|
52
|
+
matched_record = (@new_records.fetch(klass) - @matched_records[klass]).find do |record|
|
53
|
+
expected_attrs.all? {|k,v| values_match?(v, record.public_send(k))}
|
54
|
+
end
|
55
|
+
if matched_record
|
56
|
+
@matched_records[klass] << matched_record
|
57
|
+
else
|
58
|
+
incorrect[klass] << expected_attrs
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
@unmatched_records = @matched_records.map {|klass, records| [klass, @new_records[klass] - records]}.to_h.reject {|k,v| v.empty?}
|
63
|
+
return false if @incorrect_attributes.any?
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
begin
|
68
|
+
@which_block && @which_block.call(@new_records)
|
69
|
+
rescue RSpec::Expectations::ExpectationNotMetError => e
|
70
|
+
@which_failure = e
|
71
|
+
end
|
72
|
+
|
73
|
+
@which_failure.nil?
|
74
|
+
end
|
75
|
+
|
76
|
+
failure_message do
|
77
|
+
if @incorrect_counts.present?
|
78
|
+
@incorrect_counts.map do |klass, counts|
|
79
|
+
"The block should have created #{count_str(klass, counts[:expected])}, but created #{counts[:actual]}."
|
80
|
+
end.join(" ")
|
81
|
+
elsif @incorrect_attributes.present?
|
82
|
+
"The block should have created:\n" +
|
83
|
+
@expected_attributes.map do |klass, attrs|
|
84
|
+
" #{attrs.count} #{klass} with these attributes:\n" +
|
85
|
+
attrs.map{|a| " #{a.inspect}"}.join("\n")
|
86
|
+
end.join("\n") +
|
87
|
+
"\nDiff:" +
|
88
|
+
@incorrect_attributes.map do |klass, attrs|
|
89
|
+
"\n Missing #{attrs.count} #{klass} with these attributes:\n" +
|
90
|
+
attrs.map{|a| " #{a.inspect}"}.join("\n")
|
91
|
+
end.join("\n") +
|
92
|
+
@unmatched_records.map do |klass, records|
|
93
|
+
"\n Extra #{records.count} #{klass} with these attributes:\n" +
|
94
|
+
records.map do |r|
|
95
|
+
attrs = @all_attributes[klass].each_with_object({}) {|attr, attrs| attrs[attr] = r.public_send(attr)}
|
96
|
+
" #{attrs.inspect}"
|
97
|
+
end.join("\n")
|
98
|
+
end.join("\n")
|
99
|
+
elsif @which_failure
|
100
|
+
@which_failure
|
101
|
+
else
|
102
|
+
"Unknown error"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
failure_message_when_negated do
|
107
|
+
record_counts.map do |klass, expected_count|
|
108
|
+
"The block should not have created #{count_str(klass, expected_count)}, but created #{expected_count}."
|
109
|
+
end.join(" ")
|
110
|
+
end
|
111
|
+
|
112
|
+
def count_str(klass, count)
|
113
|
+
"#{count} #{klass.name.pluralize(count)}"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
RSpec::Matchers.alias_matcher :create, :create_records
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_record_block_matchers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nathan Wallace
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-04-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -142,10 +142,12 @@ files:
|
|
142
142
|
- bin/setup
|
143
143
|
- db/config.yml
|
144
144
|
- db/migrate/20150225014908_create_people.rb
|
145
|
+
- db/migrate/20151017231107_create_dogs.rb
|
145
146
|
- db/schema.rb
|
146
147
|
- lib/active_record_block_matchers.rb
|
147
148
|
- lib/active_record_block_matchers/config.rb
|
148
|
-
- lib/active_record_block_matchers/
|
149
|
+
- lib/active_record_block_matchers/create_a_new_matcher.rb
|
150
|
+
- lib/active_record_block_matchers/create_records_matcher.rb
|
149
151
|
- lib/active_record_block_matchers/version.rb
|
150
152
|
homepage: https://github.com/nwallace/active_record_block_matchers
|
151
153
|
licenses:
|
@@ -167,7 +169,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
167
169
|
version: '0'
|
168
170
|
requirements: []
|
169
171
|
rubyforge_project:
|
170
|
-
rubygems_version: 2.
|
172
|
+
rubygems_version: 2.5.1
|
171
173
|
signing_key:
|
172
174
|
specification_version: 4
|
173
175
|
summary: Additional RSpec custom matchers for ActiveRecord
|