datum 4.1.3 → 4.1.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ee1edf4f2d3f9a7bc8386ff765d25db81622a113
4
- data.tar.gz: 65404192ce0bb1bf2bbb12aa6d3d610a7a21282d
3
+ metadata.gz: dd71e45fa21da8dee623296b6364276aea53e046
4
+ data.tar.gz: c62a33f1b5d2d0372eae2dd79f3d1a88a564c9d2
5
5
  SHA512:
6
- metadata.gz: c09f53fb308eef2919e2ab4c36b33416fd8bbebabb73796e47020b7fbc47c6f768ce677040b511d6b506c5a3db9fe7d254b161f820b691c4b8ad02f17b18b6bd
7
- data.tar.gz: d25b032dd1e5e285bf2bb69a015eef7ccf650544f3191af9b637f9886369d2af4ea1e8cde8042451d2b02d3301899a4f66900656f0e582d43131d898d87d075e
6
+ metadata.gz: 656998cde9a7af4d4548e1d77e2f25ca4a09745479ef4840d680ae62a9115ff87395bd0ad5e8186fe5f0db995c9c5bad272773b1b17c09da2ffa03468556e07e
7
+ data.tar.gz: f5d4b814485362e88872ee61b54f851c1033b92d5ffe3edb0571cc3a89d0c0c3d75bfbbe3e09e5e7edcf84208f17b81c97dcb151b7b090babf6798a3f18b8dfb
data/MIT-LICENSE ADDED
@@ -0,0 +1,44 @@
1
+ Copyright 2015 Tyemill
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ #### From https://github.com/iconara/immutable_struct/blob/master/LICENSE
23
+ #### Provided due to use of immutable_struct/blob/master/lib/immutable_struct.rb
24
+
25
+ Copyright (c) 2010 Theo Hultberg
26
+
27
+ Permission is hereby granted, free of charge, to any person obtaining
28
+ a copy of this software and associated documentation files (the
29
+ "Software"), to deal in the Software without restriction, including
30
+ without limitation the rights to use, copy, modify, merge, publish,
31
+ distribute, sublicense, and/or sell copies of the Software, and to
32
+ permit persons to whom the Software is furnished to do so, subject to
33
+ the following conditions:
34
+
35
+ The above copyright notice and this permission notice shall be
36
+ included in all copies or substantial portions of the Software.
37
+
38
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
39
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
40
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
41
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
42
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
43
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
44
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,311 @@
1
+ ## Datum: A flexible data-driven test solution for Rails
2
+
3
+ ### Synopsis
4
+ Datum includes an easy-to-use mechanism for generating test cases based on data attributes. For more granular control when seeding the test database, Datum introduces Scenarios. By combining these features, Datum can be extremely useful and fun.
5
+
6
+
7
+ ### Feature List
8
+ **1. Tests coupled with datasets**: Datum adds a data-driven extension to the default Rails testing infrastructure. When a data_test is defined, it is coupled with a dataset you define. For each dataset, a test case is generated greatly simplifying adding and removing additional cases.
9
+
10
+ **2. On-demand test database seeding**: Datum Scenarios are a per-test or per-test-class database seeding mechanism. Each Scenario can use any set of Models in a single file and can be self-contained or referenced via other Scenarios.
11
+
12
+ ### Installing
13
+ Update your Gemfile with:
14
+
15
+ ```ruby
16
+ gem 'datum'
17
+ ```
18
+
19
+ Next run the bundle command:
20
+
21
+ ```console
22
+ bundle install
23
+ ```
24
+
25
+ To create the default datum directories:
26
+
27
+ ```console
28
+ rake datum:install
29
+ ```
30
+
31
+ ### Simple Examples
32
+
33
+ #### data_test: Data Driven Tests for Rails
34
+ To get started, we'll look at a simple Person Model app/models/person.rb:
35
+
36
+ ```ruby
37
+ class Person < ActiveRecord::Base
38
+
39
+ validates_presence_of :first_name, :last_name
40
+
41
+ # "John Doe" from first_name: "John", last_name: "Doe"
42
+ def name
43
+ "#{self.first_name} #{self.last_name}"
44
+ end
45
+
46
+ # "John D." from first_name: "John" last_name: "Doe"
47
+ def short_name
48
+ "#{self.first_name} #{self.last_name.capitalize[0]}."
49
+ end
50
+ end
51
+ ```
52
+
53
+ Now let's create a test test/models/person_test.rb:
54
+
55
+ ```ruby
56
+ require 'test_helper'
57
+ class PersonTest < ActiveSupport::TestCase
58
+ test 'should confirm short_name' do
59
+ person = Person.create first_name: "Marge", last_name: "Simpson"
60
+ assert_equal "Marge S.", person.short_name
61
+ end
62
+ end
63
+ ```
64
+
65
+ Executing the test:
66
+
67
+ ```console
68
+ # Running:
69
+
70
+ .
71
+
72
+ 1 runs, 1 assertions, 0 failures, 0 errors, 0 skips
73
+ ```
74
+
75
+ To convert our test to be data-driven - test/models/person_test.rb:
76
+
77
+ ```ruby
78
+ require 'test_helper'
79
+ class PersonTest < ActiveSupport::TestCase
80
+ #test 'should confirm short_name' do
81
+ # person = Person.create first_name: "Marge", last_name: "Simpson"
82
+ # assert_equal "Marge S.", person.short_name
83
+ #end
84
+
85
+ data_test 'should_confirm_shortname' do
86
+ person = Person.create first_name: @datum.first_name, last_name: @datum.last_name
87
+ assert_equal @datum.short_name, person.short_name
88
+ end
89
+ end
90
+ ```
91
+
92
+ In the data_test, the fixed values of the original test have been replaced with the usage of the @datum.[attribute] variables. For each dataset defined, the data_test will be called and @datum will provide access.
93
+
94
+ Next, we'll define our Datum and data in test/datum/data/should_confirm_shortname.rb:
95
+
96
+ ```ruby
97
+ # Sub-class the Datum struct with attributes we need for our test
98
+ SimpleShortName = Datum.new(:first_name, :last_name, :short_name)
99
+
100
+ # Define instances for our test cases
101
+ SimpleShortName.new "Marge", "Simpson", "Marge S."
102
+ SimpleShortName.new "Homer", "Simpson", "Homer S."
103
+ ```
104
+
105
+ Executing:
106
+
107
+ ```console
108
+ # Running:
109
+
110
+ ..
111
+
112
+ 2 runs, 2 assertions, 0 failures, 0 errors, 0 skips
113
+ ```
114
+
115
+ Adding more datasets and thus more generated test cases test/datum/data/should_confirm_shortname.rb:
116
+
117
+ ```ruby
118
+ SimpleShortName = Datum.new(:first_name, :last_name, :short_name)
119
+
120
+ # Define instances for our test cases
121
+ SimpleShortName.new "Marge", "Simpson", "Marge S."
122
+ SimpleShortName.new "Homer", "Simpson", "Homer S."
123
+ SimpleShortName.new "Lisa", "Simpson", "Lisa S."
124
+ SimpleShortName.new "Bart", "Simpson", "Bart S."
125
+ SimpleShortName.new "Maggie", "Simpson", "Maggie S."
126
+ ```
127
+
128
+ ```console
129
+ # Running:
130
+
131
+ .....
132
+
133
+ 5 runs, 5 assertions, 0 failures, 0 errors, 0 skips
134
+ ```
135
+
136
+ #### Scenarios: On-demand Test Database Seeding
137
+ To get started, we'll look at a simple Person Model app/models/person.rb:
138
+
139
+ ```ruby
140
+ class Person < ActiveRecord::Base
141
+
142
+ validates_presence_of :first_name, :last_name
143
+
144
+ # "John Doe" from first_name: "John", last_name: "Doe"
145
+ def name
146
+ "#{self.first_name} #{self.last_name}"
147
+ end
148
+
149
+ # "John D." from first_name: "John" last_name: "Doe"
150
+ def short_name
151
+ "#{self.first_name} #{self.last_name.capitalize[0]}."
152
+ end
153
+ end
154
+ ```
155
+
156
+ Now let's create a test test/models/person_test.rb:
157
+
158
+ ```ruby
159
+ require 'test_helper'
160
+ class PersonTest < ActiveSupport::TestCase
161
+ test 'should confirm short_name' do
162
+ person = Person.create first_name: "Marge", last_name: "Simpson"
163
+ assert_equal "Marge S.", person.short_name
164
+ end
165
+ end
166
+ ```
167
+
168
+ Instead of using a Fixture or defining our variables in-line, we'll modify our test to use a Scenario. First, let's define the Scenario test/datum/scenarios/simpsons_scenario.rb:
169
+
170
+ ```ruby
171
+ @marge = Person.create(first_name: "Marge", last_name: "Simpson")
172
+ @homer = Person.create(__clone(@marge, {first_name: "Homer"}))
173
+ ```
174
+
175
+ Now, let's modify our test, test/models/person_test.rb:
176
+
177
+ ```ruby
178
+ require 'test_helper'
179
+ class PersonTest < ActiveSupport::TestCase
180
+ test 'should confirm short_name' do
181
+ process_scenario :simpsons_scenario
182
+ assert_equal "Marge S.", @marge.short_name
183
+ assert_equal "Homer S.", @homer.short_name
184
+ end
185
+ end
186
+ ```
187
+
188
+ #### Data-Driven Tests Combined with On-Demand Test DB Seeding
189
+ To get started, we'll look at a simple Person Model app/models/person.rb:
190
+
191
+ ```ruby
192
+ class Person < ActiveRecord::Base
193
+
194
+ validates_presence_of :first_name, :last_name
195
+
196
+ # "John Doe" from first_name: "John", last_name: "Doe"
197
+ def name
198
+ "#{self.first_name} #{self.last_name}"
199
+ end
200
+
201
+ # "John D." from first_name: "John" last_name: "Doe"
202
+ def short_name
203
+ "#{self.first_name} #{self.last_name.capitalize[0]}."
204
+ end
205
+ end
206
+ ```
207
+
208
+ Now let's define a data_test test/models/person_test.rb:
209
+
210
+ ```ruby
211
+ require 'test_helper'
212
+ class PersonTest < ActiveSupport::TestCase
213
+ data_test 'should_confirm_shortname' do
214
+ person = Person.create first_name: @datum.first_name, last_name: @datum.last_name
215
+ assert_equal @datum.short_name, person.short_name
216
+ end
217
+ end
218
+ ```
219
+
220
+ Let's add some initial data test/datum/data/should_confirm_shortname.rb:
221
+
222
+ ```ruby
223
+ SimpleShortName = Datum.new(:first_name, :last_name, :short_name)
224
+
225
+ # Define instances for our test cases
226
+ m = SimpleShortName.new "Marge", "Simpson", "Marge S."
227
+ SimpleShortName.new "Homer", m.last_name, "Homer S."
228
+ SimpleShortName.new "Lisa", m.last_name, "Lisa S."
229
+ SimpleShortName.new "Bart", m.last_name, "Bart S."
230
+ SimpleShortName.new "Maggie", m.last_name, "Maggie S."
231
+ ```
232
+
233
+ Now let's define a Scenario test/datum/scenarios/simpsons_scenario.rb:
234
+
235
+ ```ruby
236
+ @marge = Person.create(first_name: "Marge", last_name: "Simpson")
237
+ @homer = Person.create(__clone(@marge, {first_name: "Homer"}))
238
+ @lisa = Person.create(__clone(@marge, {first_name: "Lisa"}))
239
+ @bart = Person.create(__clone(@marge, {first_name: "Bart"}))
240
+ @maggie = Person.create(__clone(@marge, {first_name: "Maggie"}))
241
+ ```
242
+
243
+ Now let's update our data_test to make use of our Scenario test/models/person_test.rb:
244
+
245
+ ```ruby
246
+ require 'test_helper'
247
+ class PersonTest < ActiveSupport::TestCase
248
+ data_test 'should_confirm_shortname' do
249
+ process_scenario :simpsons_scenario
250
+ person = self.instance_variable_get("@#{@datum.first_name.downcase}")
251
+ assert_equal @datum.first_name, person.first_name
252
+ assert_equal @datum.last_name, person.last_name
253
+ assert_equal @datum.short_name, person.short_name
254
+ end
255
+ end
256
+ ```
257
+
258
+ Executing the test:
259
+
260
+ ```console
261
+ # Running:
262
+
263
+ .....
264
+
265
+ 5 runs, 15 assertions, 0 failures, 0 errors, 0 skips
266
+ ```
267
+
268
+ ### Real-World Examples (Not Finished)
269
+ A Model Test to verify addresses from different countries:
270
+
271
+ ```ruby
272
+ require 'test_helper'
273
+ class AddressLabelTest < ActiveSupport::TestCase
274
+
275
+ # makes use of 'AddressLabel' Model
276
+ data_test 'should_verify_address_labels' do
277
+ # label = AddressLabel.create
278
+
279
+ #assert_equal @datum.
280
+ #assert_equal @datum.
281
+ #assert_equal @datum.
282
+ end
283
+ end
284
+ ```
285
+
286
+ A Functional Test to verify CRUD with different permissions:
287
+
288
+ ```ruby
289
+ require 'test_helper'
290
+ class AddressLabelTest < ActiveSupport::TestCase
291
+
292
+ # makes use of 'AddressLabel' Model
293
+ data_test 'should_verify_address_labels' do
294
+ # label = AddressLabel.create
295
+
296
+ #assert_equal @datum.
297
+ #assert_equal @datum.
298
+ #assert_equal @datum.
299
+ end
300
+ end
301
+ ```
302
+
303
+
304
+
305
+ ### License
306
+
307
+ MIT License. Copyright 2012-2015 Tyemill. http://tyemill.com
308
+
309
+ You are not granted rights or licenses to the trademarks of Tyemill, including without limitation the Datum name or logo.
310
+
311
+ Datum struct uses ImprovedStruct and ImmutableStruct which are derivitive works based on ImmutableStruct by Theo Hultberg. This great class can be found here: [Immutable Struct](https://github.com/iconara/immutable_struct)
data/Rakefile ADDED
@@ -0,0 +1,32 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Datum'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+
18
+
19
+
20
+ Bundler::GemHelper.install_tasks
21
+
22
+ require 'rake/testtask'
23
+
24
+ Rake::TestTask.new(:test) do |t|
25
+ t.libs << 'lib'
26
+ t.libs << 'test'
27
+ t.pattern = 'test/**/*_test.rb'
28
+ t.verbose = false
29
+ end
30
+
31
+
32
+ task default: :test
@@ -0,0 +1,81 @@
1
+ require "datum/helpers"
2
+
3
+ module Datum
4
+ # A Container holds attributes for a single data test.
5
+ #
6
+ # A data_test definition references a specific file in test/datum/data. When
7
+ # the data file is loaded, each Datum created is associated with a Container
8
+ # which in-turn is associated with the data_test.
9
+ #
10
+ # Container references are stored in Datum::containers
11
+ class Container
12
+
13
+ # @!attribute [r] data_method_name
14
+ # The name of the data test method
15
+ # @return [String]
16
+ attr_reader :data_method_name
17
+
18
+ # @!attribute [r] test_instance
19
+ # The ActiveSupport::TestCase instance of the data test
20
+ # @return [ActiveSupport::TestCase]
21
+ attr_reader :test_instance
22
+
23
+ # @!attribute [r] count
24
+ # The total number of test cases generated for the data method
25
+ # @return [Fixnum]
26
+ def count; @loaded_data.count + @invoked_data.count; end;
27
+
28
+ # @!attribute [r] data
29
+ # A Hash of data elements, datum structs for the test case
30
+ # @return [Hash]
31
+ def data; @loaded_data.merge(@invoked_data); end
32
+
33
+ alias_method :length, :count
34
+ alias_method :size, :count
35
+ alias_method :test_count, :count
36
+
37
+ # @!visibility private
38
+ # Creates a Hash key formatted for use with a Container.
39
+ # @param [TestCase] tst_instance The TestCase instance for the data_test
40
+ # @param [String] data_method_name The name of the data_test method
41
+ # @return [String]
42
+ def self.key tst_instance, data_method_name
43
+ Helpers.build_key(tst_instance, data_method_name)
44
+ end
45
+
46
+ # @!visibility private
47
+ # @param [String] data_method_name The name of test method to be called
48
+ # @param [TestCase] tst_instance The instance containing the data_method_name
49
+ def initialize(data_method_name, tst_instance)
50
+ @data_method_name = data_method_name; @test_instance = tst_instance
51
+ @loaded_data = {}; @invoked_data = {}
52
+ ::Datum.send(:add_container, self,
53
+ Container.key(@test_instance, @data_method_name))
54
+ end
55
+
56
+ private
57
+
58
+ def add_datum datum
59
+ test_name = Helpers.build_test_name(data_method_name, test_count + 1)
60
+ @loaded_data[Datum.key(test_instance, test_name)] = datum
61
+ add_data_test test_name
62
+ [count, test_name]
63
+ end
64
+
65
+ def invoke_datum key, tst_case
66
+ @invoked_data[key] = datum = @loaded_data.delete(key)
67
+ tst_case.instance_variable_set :@datum, datum
68
+ tst_case.send datum.container.data_method_name
69
+ end
70
+
71
+ def add_data_test test_name
72
+ test_instance.send(:define_method, test_name) do
73
+ datum_key = Datum.key(nm = self.class.to_s, __method__)
74
+ container_key = Container.key(nm,
75
+ Helpers.data_method_from_test_name(__method__))
76
+ ::Datum.containers[container_key].send(:invoke_datum, datum_key, self)
77
+ end
78
+ end
79
+
80
+ end
81
+ end
@@ -0,0 +1,69 @@
1
+ require "datum/container"
2
+ require "datum/helpers"
3
+ require "plan9/structures"
4
+
5
+ module Datum
6
+ # Datum is an Immutable Struct to be used for creating test cases.
7
+ # @!attribute [r] datum_id
8
+ # The index of the test case and the data from it's Datum
9
+ # @return [Fixnum]
10
+ # @!attribute [r] test_method_name
11
+ # The name of the test case generated
12
+ # @return [String]
13
+ # @!attribute [r] container
14
+ # The Container reference which generated this Datum and test case
15
+ # @return [Container]
16
+ class Datum < Plan9::ImmutableStruct
17
+ # @!visibility private
18
+ # Creates a Hash key formatted for use with a Datum
19
+ # @param [TestCase] test_instance The TestCase instance for the test
20
+ # @param [String] test_name The name of the test
21
+ # @return [String] Datum compatible Hash key
22
+ def self.key test_instance, test_name
23
+ Helpers.build_key(test_instance, test_name)
24
+ end
25
+
26
+ # @!visibility private
27
+ def self.new(*attrs, &block)
28
+ attrs.push "datum_id"
29
+ super(*attrs, &block)
30
+ end
31
+
32
+ protected
33
+
34
+ def self.init_new(struct)
35
+ super(struct)
36
+ datumize_constructor!(struct)
37
+ struct
38
+ end
39
+
40
+ private
41
+ def self.datumize_constructor! struct
42
+
43
+ struct.class_eval do
44
+ alias_method :datum_initialize, :initialize
45
+
46
+ # @!attribute [r] test_method_name
47
+ # @return [String] The name of the test method
48
+ attr_reader :test_method_name
49
+
50
+ # @!attribute [r] container
51
+ # @return [Container] A reference to the Container of this Datum
52
+ attr_reader :container
53
+
54
+ # @!visibility private
55
+ def initialize(*atrs)
56
+ dtm_id = configure_attributes
57
+ is_hash_case?(*atrs) ? atrs.first[:datum_id] = dtm_id : atrs.push(dtm_id)
58
+ datum_initialize(*atrs)
59
+ end
60
+
61
+ private
62
+ def configure_attributes
63
+ @container = ::Datum.send(:current_container)
64
+ (dtm_id, @test_method_name = @container.send(:add_datum, self))[0]
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,67 @@
1
+ module Datum
2
+
3
+ # @!visibility private
4
+ # Various helper functions
5
+ class Helpers
6
+ class << self
7
+ # @!visibility private
8
+ # @param [String] test_name Test case name generated from data_test usage
9
+ # @return [int] The index of the data_test / datum_id
10
+ def index_from_test_name test_name
11
+ ((test_name.to_s.split('_')[-1]).to_i)
12
+ end
13
+
14
+ # @!visibility private
15
+ # @param [String] test_name Test case name generated from data_test usage
16
+ # @return [String] Name of the data_test method which generated the test
17
+ def data_method_from_test_name test_name
18
+ test_name.slice(/(?<=_).*(?=_)/)
19
+ end
20
+
21
+ # @!visibility private
22
+ # Test name for current test case, index, data_test method
23
+ # @param [String] data_method_name The name of the data_test method
24
+ # @param [int] counter The index / datum_id of the current test case
25
+ # @return [String]
26
+ def build_test_name data_method_name, counter
27
+ "test_#{data_method_name}_#{counter}"
28
+ end
29
+
30
+ # @!visibility private
31
+ # @param [TestCase] test_instance The ActiveSupport::TestCase instance
32
+ # @param [String] method The name of the method
33
+ # @return [String] A key for usage in Datum-compatible Hash instances
34
+ def build_key test_instance, method
35
+ "#{test_instance}_#{method}"
36
+ end
37
+
38
+ # @!visibility private
39
+ # @param [String] file_name The name of the file to read
40
+ # @param [Pathname] directory A Pathname representing the file's directory
41
+ # @param [String] ext Optional extention of the file (default: '.rb')
42
+ # @return [String] The file's contents
43
+ def read_file file_name, directory, ext = ".rb"
44
+ File.read directory.join("#{file_name}#{ext}")
45
+ end
46
+
47
+ # @!visibility private
48
+ # Reads a ruby file and eval's it's contents in the context of the Binding
49
+ # @param [String] file_name The name of the file to import
50
+ # @param [Pathname] directory A Pathname representing the file's directory
51
+ # @param [Binding] current_binding Context at a particular code location
52
+ def import_file file_name, directory, current_binding
53
+ eval(read_file(file_name, directory), current_binding)
54
+ end
55
+
56
+ # @!visibility private
57
+ # @param [ActiveRecord::Base] resource An ActiveRecord Model instance
58
+ # @param [Hash] override_hash Hash of attributes / values to override from
59
+ # @return [Hash] Hash of attributes from provided resource
60
+ def clone_resource resource, override_hash = nil
61
+ override_hash.nil? ? resource.dup.attributes.with_indifferent_access :
62
+ resource.dup.attributes.merge(
63
+ override_hash.with_indifferent_access).with_indifferent_access
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,24 @@
1
+ module Datum
2
+ # @!visibility private
3
+ VERSION = "4.1.4"
4
+ end
5
+
6
+
7
+ ## 0.8.1 - 0.9.2
8
+ ## Original datum, proof-of-concept
9
+ ##
10
+ ## 4.0.0
11
+ ## Full rewrite, updated with latest concepts and code -- Still in Testing
12
+ ##
13
+ ## 4.0.x
14
+ ## Several small updates (readme, etc) -- Readme still needs work.
15
+ ## 4.0.6
16
+ ## Seems like the build process was dead on arrival? Trying a re-build .7
17
+ ## 4.0.7
18
+ ## Does not seem to have fixed the issue. :(
19
+ ## 4.0.8
20
+ ## Removing railtie reference
21
+ ## 4.0.9
22
+ ## Removing railtie and rake task
23
+ ## 4.1.0
24
+ ## Removing files.
data/lib/datum.rb ADDED
@@ -0,0 +1,45 @@
1
+
2
+ require "datum/helpers"
3
+ require "datum/datum"
4
+ require "datum/container"
5
+ require "support/test"
6
+ require "support/scenario"
7
+
8
+ # Datum is a flexible data-driven test solution for Rails.
9
+ #
10
+ # Datum's primary features include defining data-driven tests via the
11
+ # data_test method and Scenarios, a load-on-demand mechanism for seeding the
12
+ # test database.
13
+ module Datum
14
+ @@all_containers, @@scenario_path, @@data_path, @@datum_path = nil
15
+
16
+ class << self
17
+
18
+ # @!attribute [r] path
19
+ # Fully qualified path for the root of datum directory
20
+ # @return [Pathname]
21
+ def path; @@datum_path ||= Rails.root.join('test', 'datum'); end
22
+
23
+ # @!attribute [r] data_path
24
+ # Fully qualified path for the datum/data directory
25
+ # @return [Pathname]
26
+ def data_path; @@data_path ||= ::Datum.path.join('data'); end
27
+
28
+ # @!attribute [r] scenario_path
29
+ # Fully qualified path for the datum/scenarios directory
30
+ # @return [Pathname]
31
+ def scenario_path; @@scenario_path ||= ::Datum.path.join('scenarios'); end
32
+
33
+ # @!attribute [r] containers
34
+ # Hash of all loaded Containers
35
+ # @return [Hash]
36
+ def containers; @@all_containers ||= {}; end
37
+
38
+ private
39
+ def add_container container, key
40
+ ::Datum.containers[key] = container
41
+ ::Datum.instance_variable_set(:"@current_container", container)
42
+ end
43
+ def current_container; @current_container; end;
44
+ end
45
+ end
@@ -0,0 +1,76 @@
1
+ # @!visibility private
2
+ module Plan9 # Module for simple code reuse between various projects.
3
+ # @!visibility private
4
+ class ImprovedStruct # A few improvements to a Ruby Struct
5
+ #
6
+ # Re-organized slightly, this code is reused from 'ImmutableStruct'
7
+ # by Theo Hultberg. See https://github.com/iconara/immutable_struct
8
+ # Copyright notice mentioned in the LICENSE file.
9
+ #
10
+ # @!visibility private
11
+ def self.new(*attrs, &block)
12
+ init_new(Struct.new(*attrs, &block))
13
+ end
14
+
15
+ protected
16
+ def self.init_new(struct)
17
+ optionalize_constructor!(struct)
18
+ extend_dup!(struct)
19
+ struct
20
+ end
21
+
22
+ private
23
+ def self.optionalize_constructor!(struct)
24
+ struct.class_eval do
25
+ alias_method :struct_initialize, :initialize
26
+
27
+ def initialize(*attrs)
28
+ if is_hash_case?(*attrs)
29
+ struct_initialize(*members.map { |m| attrs.first[m.to_sym] })
30
+ else
31
+ struct_initialize(*attrs)
32
+ end
33
+ end
34
+
35
+ protected
36
+ def is_hash_case?(*a)
37
+ # @return (bool) true if attrs are Hash, false otherwise
38
+ members.size > 1 && a && a.size == 1 && a.first.instance_of?(Hash)
39
+ end
40
+ end
41
+ end
42
+
43
+ def self.extend_dup!(struct)
44
+ struct.class_eval do
45
+ def dup(overrides={})
46
+ self.class.new(to_h.merge(overrides))
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ # @!visibility private
53
+ class ImmutableStruct < ImprovedStruct # A read-only Struct
54
+ # Re-organized slightly, this code is reused from 'ImmutableStruct'
55
+ # by Theo Hultberg. See https://github.com/iconara/immutable_struct
56
+ # Copyright notice mentioned in the LICENSE file.
57
+
58
+ protected
59
+
60
+ def self.init_new(struct)
61
+ make_immutable!(struct)
62
+ super(struct)
63
+ end
64
+
65
+ private
66
+
67
+ def self.make_immutable!(struct)
68
+ # removes the member= method, to prevent write
69
+ struct.send(:undef_method, "[]=".to_sym)
70
+ struct.members.each do |member|
71
+ struct.send(:undef_method, "#{member}=".to_sym)
72
+ end
73
+ end
74
+
75
+ end
76
+ end
@@ -0,0 +1,42 @@
1
+
2
+ # From a scenario file, imports an existing scenario's code into the current
3
+ # Binding context of the current scenario.
4
+ #
5
+ # @param [Symbol, String] scenario_name The name of a scenario file
6
+ # @return [void]
7
+ #
8
+ # @example Using __import
9
+ # # test/datum/scenarios/springfield_police_scenario.rb
10
+ # @clancy = Person.create(first_name: "Clancy", last_name: "Wiggum")
11
+ # @eddie = Person.create(first_name: "Eddie", last_name: "Police-Officer")
12
+ #
13
+ # # test/datum/scenarios/extended_simpsons_scenario.rb
14
+ # @homer = Person.create(first_name: "Homer", last_name: "Simpson")
15
+ # @marge = Person.create(__clone(@homer, {first_name: "Marge"}))
16
+ # @bart = Person.create(__clone(@homer, {first_name: "Marge"}))
17
+ #
18
+ # __import :springfield_police_scenario # will give us clancy, eddie
19
+ #
20
+ # # Using @eddie from imported :springfield_police_scenario to
21
+ # # define @lou's last_name
22
+ # @lou = Person.create first_name: "Lou", last_name: @eddie.last_name
23
+ def __import scenario_name
24
+ ::Datum::Helpers.import_file scenario_name, ::Datum.scenario_path, binding
25
+ end
26
+
27
+ # From a scenario file, clones the attributes of an existing instance and
28
+ # overrides as specified.
29
+ #
30
+ # @param [ActiveRecord::Base] resource An ActiveRecord Model instance
31
+ # @param [Hash] override_hash Hash of attributes / values to override from
32
+ #
33
+ # @return [Hash] Hash of attributes from provided resource
34
+ #
35
+ # @example Using __clone
36
+ # # test/datum/scenarios/simpsons_scenario.rb
37
+ # # any code included in this file gets loaded when calling process_scenario
38
+ # @homer = Person.create(first_name: "Homer", last_name: "Simpson")
39
+ # @marge = Person.create(__clone(@homer, {first_name: "Marge"}))
40
+ def __clone resource, override_hash = nil
41
+ ::Datum::Helpers.clone_resource resource, override_hash
42
+ end
@@ -0,0 +1,80 @@
1
+ require "datum/helpers"
2
+ require "datum/container"
3
+ require "datum/datum"
4
+
5
+ # Adds the process_scenario method to ActiveSupport::TestCase and includes
6
+ # the Datum module
7
+ # @note Supports most extending test types (functional, integration, etc)
8
+ # @example Making a Scenario
9
+ # # test/datum/scenarios/simpsons_scenario.rb
10
+ # # any code included in this file gets loaded when calling process_scenario
11
+ # @homer = Person.create(first_name: "Homer", last_name: "Simpson")
12
+ # @marge = Person.create(__clone(@homer, {first_name: "Marge"}))
13
+ # @!method process_scenario(scenario_name)
14
+ # Imports a scenario file into the execution context of the current test
15
+ # @param [Symbol, String] scenario_name The name of a scenario file
16
+ # @return [void]
17
+ # @example Using process_scenario
18
+ # test "should verify basic scenario" do
19
+ # process_scenario :simpsons_scenario
20
+ # assert_not_nil @homer, "process_scenario did not define @homer"
21
+ # assert_not_nil @marge, "process_scenario did not define @marge"
22
+ # end
23
+ class ActiveSupport::TestCase
24
+ include Datum
25
+
26
+ def process_scenario scenario_name
27
+ __import(scenario_name)
28
+ end
29
+ end
30
+
31
+ # Defines a test to work in conjuction with Datum struct extensions found in
32
+ # a file with the same name in the test/datum/data directory
33
+ #
34
+ # @param [String] name Name of the file in the datum/data directory
35
+ # @param [Block] block A block of Ruby code
36
+ #
37
+ # @return [void]
38
+ #
39
+ # @example Defining a data_test
40
+ # # test/datum/data/simple_person_data.rb
41
+ #
42
+ # # first, define a sub-class of Datum to use in your test
43
+ # PersonData = Datum.new(:first_name, :last_name, :name, :short_name)
44
+ #
45
+ # # next, use your sub-class to create datasets which will be accessible to
46
+ # # your data_test as @datum
47
+ # #
48
+ # # your data can be generated, etc... here, we're keeping it simple
49
+ # homer = PersonData.new("Homer", "Simpson", "Homer Simpson", "Homer S.")
50
+ # marge = PersonData.new("Marge", homer.last_name,
51
+ # "Marge #{homer.last_name}", "Marge S.")
52
+ #
53
+ # # test/datum/scenarios/simpsons_scenario.rb
54
+ #
55
+ # @homer = Person.create(first_name: "Homer", last_name: "Simpson")
56
+ # @marge = Person.create(__clone(@homer, {first_name: "Marge"}))
57
+ #
58
+ # # test/models/person_test.rb
59
+ # require 'test_helper'
60
+ # class PersonTest < ActiveSupport::TestCase
61
+ #
62
+ # # this data method will be called once for each Datum defined in
63
+ # # test/datum/data/simple_person_data.rb
64
+ # #
65
+ # # each time this method is called @datum will reference the current
66
+ # # dataset
67
+ # data_test "simple_person_data" do
68
+ # process_scenario :simpsons_scenario
69
+ # person = self.instance_variable_get("@#{@datum.first_name.downcase}")
70
+ # assert_equal @datum.first_name, person.first_name
71
+ # assert_equal @datum.last_name, person.last_name
72
+ # assert_equal @datum.name, person.name
73
+ # assert_equal @datum.short_name, person.short_name
74
+ # end
75
+ # end
76
+ def data_test name, &block
77
+ ::Datum::Container.new(name, self)
78
+ self.send(:define_method, name, &block)
79
+ self.class_eval(::Datum::Helpers.read_file(name, ::Datum.data_path))
80
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: datum
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.3
4
+ version: 4.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tyemill
@@ -14,14 +14,14 @@ dependencies:
14
14
  name: rails
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: 4.1.7
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: 4.1.7
27
27
  - !ruby/object:Gem::Dependency
@@ -31,7 +31,7 @@ dependencies:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
- type: :runtime
34
+ type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
@@ -44,10 +44,20 @@ email:
44
44
  executables: []
45
45
  extensions: []
46
46
  extra_rdoc_files: []
47
- files: []
47
+ files:
48
+ - MIT-LICENSE
49
+ - README.md
50
+ - Rakefile
51
+ - lib/datum.rb
52
+ - lib/datum/container.rb
53
+ - lib/datum/datum.rb
54
+ - lib/datum/helpers.rb
55
+ - lib/datum/version.rb
56
+ - lib/plan9/structures.rb
57
+ - lib/support/scenario.rb
58
+ - lib/support/test.rb
48
59
  homepage: https://github.com/tyemill/datum
49
- licenses:
50
- - MIT
60
+ licenses: []
51
61
  metadata: {}
52
62
  post_install_message:
53
63
  rdoc_options: []