active_cucumber 0.0.3 → 0.0.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: b74fd70bd52ca80c98362537b7412860f8da8d04
4
- data.tar.gz: 56ec6ca577595a1d25b51f91eacea3264e48cf15
3
+ metadata.gz: cd6e427a1ea5dd697035f91e1344e7dbfe0f8717
4
+ data.tar.gz: 0998293ca8816de728f5f1b5419e350ac42f712c
5
5
  SHA512:
6
- metadata.gz: f1a7c9599fb559349b1b7b4f2ffc4ecebfe6f3100f10a8c33b872dafa8e8be46a80afdc1efcbbe0683d8938f8404c8caaf9876cc8a4f4738fae9425cc957df4c
7
- data.tar.gz: bd49642ca5cd23485265c6a548345e348f5fcbdcf2cdb5da94625e056d169791e89999340a1d00659407ec75829d2c13772b6038638050d3d0b9c30f593f79d8
6
+ metadata.gz: 0c2ce7cc37de5e7bd600080238d73f8c10f7db99b623db4a51208d7cef6ea66697de28b814830d732df24ea1608f3cb7b15b06e313edf7a158b6c031a4bd5c78
7
+ data.tar.gz: 12bad9a4a815ba38efefed654a9175cf276317d6aea7c9f658a9e7c45db9fe4c9cfea613a6145338ac164f28c4f78b8bd9b44e879ae5a2022bde47e3018f36bc
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- active_cucumber (0.0.3)
4
+ active_cucumber (0.0.4)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -1,8 +1,101 @@
1
1
  # ActiveCucumber [![Circle CI](https://circleci.com/gh/Originate/active_cucumber.svg?style=shield)](https://circleci.com/gh/Originate/active_cucumber)
2
2
 
3
- High-level Cucumber helpers for testing
3
+ High-level Cucumber helpers for [creating](#creating-database-records)
4
+ and [verifying](#verifying-database-records)
4
5
  [ActiveRecord](http://guides.rubyonrails.org/active_record_basics.html)-based
5
- database applications using Cucumber tables.
6
+ database entries using Cucumber tables.
7
+
8
+
9
+ ## Creating database records
10
+
11
+ Creating simple fields works out of the box.
12
+ Let's assume we have an application that stores TV shows and their episodes,
13
+ and our tests contain this Cucumber table:
14
+
15
+ ```cucumber
16
+ Given the episodes:
17
+ | NAME | YEAR |
18
+ | Encounter at Farpoint | 1987 |
19
+ | All Good Things | 1994 |
20
+ ```
21
+
22
+ Implementing this step looks like:
23
+
24
+ ```ruby
25
+ Given(/^the episodes:$/) do |table|
26
+ ActiveCucumber.create_many Episode, table
27
+ end
28
+ ```
29
+
30
+ ### Transforming values
31
+
32
+ Let's say our data model also contains a `Series` class
33
+ (an episode belongs to a series, and a series has many episodes).
34
+ We want to also define (and if necessary create) the series that an episode belongs to:
35
+
36
+ ```cucumber
37
+ Given the episodes:
38
+ | SERIES | NAME |
39
+ | Star Trek TNG | Encounter at Farpoint |
40
+ | Star Trek TNG | All Good Things |
41
+ ```
42
+
43
+ ActiveCucumber doesn't require custom step definitions here.
44
+ To make this work, tell ActiveCucumber how to convert particular Cucumber table fields
45
+ into ActiveRecord attributes via a `Creator` class:
46
+
47
+ ```ruby
48
+ class EpisodeCreator < ActiveCucumber::Creator
49
+
50
+ def value_for_series series_name
51
+ Series.find_by(name: series_name) || FactoryGirl.create(:series, name: series_name)
52
+ end
53
+
54
+ end
55
+ ```
56
+
57
+ ActiveCucumber automatically uses creator classes that follow the given naming schema:
58
+ * name is `<class name>Creator`
59
+ * method names are `value_for_<attribute name>`
60
+
61
+
62
+ ### Other columns
63
+
64
+ Cucumber tables can contain columns that provide other test data,
65
+ and don't correspond to attributes on the created object.
66
+
67
+ ```cucumber
68
+ Given the episodes:
69
+ | GENRE | SERIES | NAME |
70
+ | Science Fiction | Star Trek TNG | Encounter at Farpoint |
71
+ | Comedy | The Big Bang Theory | The Big Bran Hypothesis |
72
+ ```
73
+
74
+ A `Genre` has many series, and a series belongs to a genre.
75
+ Episodes are not directly associated with genres.
76
+
77
+ ```ruby
78
+ class EpisodeCreator < ActiveCucumber::Creator
79
+
80
+ def value_for_genre genre_name
81
+ @genre = Genre.find_by(name: genre_name) || FactoryGirl.create(:genre, name: genre_name)
82
+ delete :genre
83
+ end
84
+
85
+ def value_for_show show_name
86
+ Show.find_by(name: show_name) || FactoryGirl.create(:show, name: show_name, genre: @genre)
87
+ end
88
+
89
+ end
90
+ ```
91
+
92
+ Creators decorate the data structure that
93
+ is sent to FactoryGirl to create the record.
94
+ This means `self` inside creator methods behaves like a Hash
95
+ that is pre-populated with the Cucumber table data.
96
+ You can modify this hash, use other field values,
97
+ add or remove fields,
98
+ or store instance variables to be used later.
6
99
 
7
100
 
8
101
  ## Verifying database records
@@ -3,7 +3,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = 'active_cucumber'
6
- s.version = '0.0.3'
6
+ s.version = '0.0.4'
7
7
  s.authors = ['Kevin Goslar']
8
8
  s.email = ['kevin.goslar@gmail.com']
9
9
  s.summary = %s(ActiveRecord tools for Cucumber)
@@ -30,6 +30,20 @@ Feature: ActiveCucumber.create_many
30
30
  And the database contains the show "Star Trek TNG"
31
31
 
32
32
 
33
+ Scenario: creating associated objects that depend on other associated objects
34
+ When running "ActiveCucumber.create_many Episode, table" with this table:
35
+ | GENRE | SHOW | NAME |
36
+ | Science Fiction | Star Trek TNG | Encounter at Farpoint |
37
+ | Science Fiction | Star Trek TNG | All Good Things |
38
+ Then the database contains the shows:
39
+ | GENRE | NAME |
40
+ | Science Fiction | Star Trek TNG |
41
+ And the database contains the episodes:
42
+ | SHOW | NAME |
43
+ | Star Trek TNG | Encounter at Farpoint |
44
+ | Star Trek TNG | All Good Things |
45
+
46
+
33
47
  Scenario: complex example
34
48
  When running "ActiveCucumber.create_many Episode, table" with this table:
35
49
  | SHOW | NAME | YEAR |
@@ -6,7 +6,7 @@ Feature: ActiveCucumber.diff_all!
6
6
 
7
7
 
8
8
  Background:
9
- Given the TV episodes:
9
+ Given the episodes:
10
10
  | SHOW | NAME | YEAR |
11
11
  | Star Trek TNG | Encounter at Farpoint | 1987 |
12
12
  | Star Trek TNG | All Good Things | 1994 |
@@ -6,33 +6,33 @@ Feature: ActiveCucumber.diff_one!
6
6
 
7
7
 
8
8
  Background:
9
- Given the TV episode:
9
+ Given the episode:
10
10
  | SHOW | Star Trek TNG |
11
11
  | NAME | All Good Things |
12
12
  | YEAR | 1994 |
13
13
 
14
14
 
15
15
  Scenario: verifying string fields
16
- When running "ActiveCucumber.diff_one! @episode, table" with this table:
16
+ When running "ActiveCucumber.diff_one! @created_episode, table" with this table:
17
17
  | NAME | All Good Things |
18
18
  Then the test passes
19
19
 
20
20
 
21
21
  Scenario: verifying non-string fields
22
- When running "ActiveCucumber.diff_one! @episode, table" with this table:
22
+ When running "ActiveCucumber.diff_one! @created_episode, table" with this table:
23
23
  | YEAR | 1994 |
24
24
  Then the test passes
25
25
 
26
26
 
27
27
  Scenario: verifying associated fields
28
- When running "ActiveCucumber.diff_one! @episode, table" with this table:
28
+ When running "ActiveCucumber.diff_one! @created_episode, table" with this table:
29
29
  | SHOW | Star Trek TNG |
30
30
  | NAME | All Good Things |
31
31
  Then the test passes
32
32
 
33
33
 
34
34
  Scenario: complete table match
35
- When running "ActiveCucumber.diff_one! @episode, table" with this table:
35
+ When running "ActiveCucumber.diff_one! @created_episode, table" with this table:
36
36
  | SHOW | Star Trek TNG |
37
37
  | NAME | All Good Things |
38
38
  | YEAR | 1994 |
@@ -40,7 +40,7 @@ Feature: ActiveCucumber.diff_one!
40
40
 
41
41
 
42
42
  Scenario: providing a non-existing field
43
- When running "ActiveCucumber.diff_one! @episode, table" with this table:
43
+ When running "ActiveCucumber.diff_one! @created_episode, table" with this table:
44
44
  | NAME | All Good Things |
45
45
  | FOOBAR | 1994 |
46
46
  Then the test fails
@@ -48,7 +48,7 @@ Feature: ActiveCucumber.diff_one!
48
48
 
49
49
 
50
50
  Scenario: mismatching data in a table cell
51
- When running "ActiveCucumber.diff_one! @episode, table" with this table:
51
+ When running "ActiveCucumber.diff_one! @created_episode, table" with this table:
52
52
  | SHOW | Star Trek TOS |
53
53
  | NAME | All Good Things |
54
54
  Then the test fails
@@ -1,15 +1,12 @@
1
- Given(/^the TV episode:$/) do |table|
2
- data = ActiveCucumber.vertical_table table
3
- show = Show.find_or_create_by name: data['SHOW']
4
- @episode = show.episodes.create name: data['NAME'], year: data['YEAR']
5
- end
6
-
7
-
8
- Given(/^the TV episodes:$/) do |table|
9
- ActiveCucumber.horizontal_table(table).each do |row|
10
- show = Show.find_or_create_by name: row['SHOW']
11
- show.episodes.create name: row['NAME'], year: row['YEAR']
12
- end
1
+ Given(/^the (\w+):$/) do |class_name, table|
2
+ singular = class_name.singularize
3
+ clazz = singular.humanize.constantize
4
+ created_data = if (class_name == singular)
5
+ ActiveCucumber.create_one clazz, table
6
+ else
7
+ ActiveCucumber.create_many clazz, table
8
+ end
9
+ instance_variable_set "@created_#{class_name}", created_data
13
10
  end
14
11
 
15
12
 
@@ -38,6 +35,11 @@ Then(/^the database contains the given episodes$/) do
38
35
  end
39
36
 
40
37
 
38
+ Then(/^the database contains the (\w+):$/) do |class_name, table|
39
+ ActiveCucumber.diff_all! class_name.humanize.singularize.constantize, table
40
+ end
41
+
42
+
41
43
  Then(/^the database contains the shows? (.+)$/) do |show_names|
42
44
  expect(Show.all.map(&:name)).to match Kappamaki.from_sentence show_names
43
45
  end
@@ -46,8 +48,9 @@ end
46
48
  Then(/^the test (passes|fails)$/) do |expected_result|
47
49
  @error_checked = true
48
50
  if expected_result == 'passes' && @error_happened
49
- p @error_message
50
- p @exception
51
+ puts "\n#{@error_message}"
52
+ puts ''
53
+ @exception.backtrace.take(5).each { |trace| puts "in #{trace}" }
51
54
  end
52
55
  expect(@error_happened).to be expected_result != 'passes'
53
56
  end
@@ -15,13 +15,19 @@ ActiveRecord::Base.establish_connection(
15
15
 
16
16
 
17
17
  ActiveRecord::Schema.define do
18
+ create_table :genres, force: true do |t|
19
+ t.string :name
20
+ t.datetime 'created_at'
21
+ end
22
+
18
23
  create_table :shows, force: true do |t|
24
+ t.belongs_to :genre
19
25
  t.string :name
20
26
  t.datetime 'created_at'
21
27
  end
22
28
 
23
29
  create_table :episodes, force: true do |t|
24
- t.belongs_to :show, index: true
30
+ t.belongs_to :show
25
31
  t.string :name
26
32
  t.integer :year
27
33
  t.datetime 'created_at'
@@ -30,6 +36,10 @@ end
30
36
 
31
37
 
32
38
  FactoryGirl.define do
39
+ factory :genre do
40
+ name { Faker::Book.title }
41
+ end
42
+
33
43
  factory :show do
34
44
  name { Faker::Book.title }
35
45
  end
@@ -49,5 +59,10 @@ Before do
49
59
  end
50
60
 
51
61
  After do
52
- expect(@error_happened).to be false unless @error_checked
62
+ if @error_happened && !@error_checked
63
+ puts "\n#{@error_message}"
64
+ puts ''
65
+ @exception.backtrace.take(5).each { |trace| puts "in #{trace}" }
66
+ expect(@error_happened).to be false
67
+ end
53
68
  end
@@ -1,3 +1,3 @@
1
1
  class Episode < ActiveRecord::Base
2
- belongs_to :show, inverse_of: :episodes, required: true
2
+ belongs_to :show, required: true
3
3
  end
@@ -1,7 +1,12 @@
1
- class EpisodeCureator < ActiveCucumber::Cureator
1
+ class EpisodeCreator < ActiveCucumber::Creator
2
2
 
3
3
  def value_for_show show_name
4
- Show.find_by(name: show_name) || FactoryGirl.create(:show, name: show_name)
4
+ Show.find_by(name: show_name) || FactoryGirl.create(:show, name: show_name, genre: @genre)
5
+ end
6
+
7
+ def value_for_genre genre_name
8
+ @genre = Genre.find_by(name: genre_name) || FactoryGirl.create(:genre, name: genre_name)
9
+ delete :genre
5
10
  end
6
11
 
7
12
  end
@@ -0,0 +1,3 @@
1
+ class Genre < ActiveRecord::Base
2
+ has_many :shows
3
+ end
@@ -1,3 +1,4 @@
1
1
  class Show < ActiveRecord::Base
2
- has_many :episodes, inverse_of: :show
2
+ belongs_to :genre
3
+ has_many :episodes
3
4
  end
@@ -0,0 +1,7 @@
1
+ class ShowCucumberator < ActiveCucumber::Cucumberator
2
+
3
+ def value_for_genre
4
+ genre.name
5
+ end
6
+
7
+ end
@@ -0,0 +1,44 @@
1
+ module ActiveCucumber
2
+
3
+ # Creates ActiveRecord entries with data from given Cucumber tables.
4
+ class ActiveRecordBuilder
5
+
6
+ def initialize activerecord_class
7
+ @clazz = activerecord_class
8
+ @creator_class = creator_class
9
+ end
10
+
11
+
12
+ # Creates all entries in the given horizontal table hash
13
+ def create_many table
14
+ table.map do |row|
15
+ create_record row
16
+ end
17
+ end
18
+
19
+
20
+ # Creates a new record with the given attributes in the database
21
+ def create_record attributes
22
+ creator = @creator_class.new attributes
23
+ FactoryGirl.create @clazz, creator.factorygirl_attributes
24
+ end
25
+
26
+
27
+ private
28
+
29
+ # Returns the Cucumberator subclass to be used by this Cucumparer instance
30
+ def creator_class
31
+ creator_class_name.constantize
32
+ rescue NameError
33
+ Creator
34
+ end
35
+
36
+
37
+ # Returns the name of the Cucumberator subclass to be used by this Cucumparer instance.
38
+ def creator_class_name
39
+ "#{@clazz.name}Creator"
40
+ end
41
+
42
+ end
43
+
44
+ end
@@ -0,0 +1,59 @@
1
+ module ActiveCucumber
2
+
3
+ # Converts an attributes hash in Cucumber format
4
+ # into a hash consumable by FactoryGirl
5
+ #
6
+ # Subclasses define methods to convert particular fields.
7
+ class Creator
8
+
9
+ def initialize attributes
10
+ @attributes = attributes
11
+ end
12
+
13
+ # Returns the FactoryGirl version of this Creator's attributes
14
+ def factorygirl_attributes
15
+ symbolize_attributes!
16
+ @attributes.each do |key, value|
17
+ next unless respond_to?(method = method_name(key))
18
+ if (result = send method, value)
19
+ @attributes[key] = result if @attributes.key? key
20
+ else
21
+ @attributes.delete key
22
+ end
23
+ end
24
+ end
25
+
26
+
27
+ private
28
+
29
+ def method_missing method_name, *arguments
30
+ # This is necessary so that a Creator subclass can access
31
+ # methods of @attributes as if they were its own.
32
+ @attributes.send method_name, *arguments
33
+ end
34
+
35
+
36
+ # Returns the name of the value_for method for the given key
37
+ def method_name key
38
+ "value_for_#{key}"
39
+ end
40
+
41
+
42
+ # Converts the key given in Cucumber format into FactoryGirl format
43
+ def normalized_key key
44
+ key.downcase.parameterize.underscore.to_sym
45
+ end
46
+
47
+
48
+ # Makes the keys on @attributes be normalized symbols
49
+ def symbolize_attributes!
50
+ @attributes = {}.tap do |result|
51
+ @attributes.each do |key, value|
52
+ result[normalized_key key] = value
53
+ end
54
+ end
55
+ end
56
+
57
+ end
58
+
59
+ end
@@ -1,20 +1,23 @@
1
1
  require 'active_cucumber/cucumparer'
2
2
  require 'active_cucumber/cucumberator'
3
- require 'active_cucumber/cureator'
3
+ require 'active_cucumber/active_record_builder'
4
+ require 'active_cucumber/creator'
4
5
 
5
6
  # The main namespace for this gem
6
7
  module ActiveCucumber
7
8
 
8
9
  # Creates entries of the given ActiveRecord class
9
- # specified by the given Cucumber table
10
+ # specified by the given horizontal Cucumber table
10
11
  def self.create_many activerecord_class, cucumber_table
11
- Cureator.for(activerecord_class).create_records ActiveCucumber.horizontal_table(cucumber_table)
12
+ builder = ActiveRecordBuilder.new activerecord_class
13
+ builder.create_many ActiveCucumber.horizontal_table(cucumber_table)
12
14
  end
13
15
 
14
16
  # Creates an entry of the given ActiveRecord class
15
- # specified by the given Cucumber table
17
+ # specified by the given vertical Cucumber table
16
18
  def self.create_one activerecord_class, cucumber_table
17
- Cureator.for(activerecord_class).create_record ActiveCucumber.vertical_table(cucumber_table)
19
+ builder = ActiveRecordBuilder.new activerecord_class
20
+ builder.create_record ActiveCucumber.vertical_table(cucumber_table)
18
21
  end
19
22
 
20
23
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_cucumber
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Goslar
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-10-22 00:00:00.000000000 Z
11
+ date: 2015-10-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -220,11 +220,14 @@ files:
220
220
  - features/support/episode.rb
221
221
  - features/support/episode_cucumberator.rb
222
222
  - features/support/episode_cureator.rb
223
+ - features/support/genre.rb
223
224
  - features/support/show.rb
225
+ - features/support/show_cucumberator.rb
224
226
  - lib/active_cucumber.rb
227
+ - lib/active_cucumber/active_record_builder.rb
228
+ - lib/active_cucumber/creator.rb
225
229
  - lib/active_cucumber/cucumberator.rb
226
230
  - lib/active_cucumber/cucumparer.rb
227
- - lib/active_cucumber/cureator.rb
228
231
  homepage: https://github.com/Originate/active_cucumber
229
232
  licenses:
230
233
  - MIT
@@ -1,86 +0,0 @@
1
- module ActiveCucumber
2
-
3
- # Creates ActiveRecord entries
4
- class Cureator
5
-
6
- # Returns the Cureator instance for the given ActiveRecord class.
7
- def self.for activerecord_class
8
- cureator_class(activerecord_class).new activerecord_class
9
- end
10
-
11
-
12
- def initialize activerecord_class
13
- @clazz = activerecord_class
14
- end
15
-
16
-
17
- # Creates all entries in the given Cucumber table
18
- #
19
- # Assumes a horizontal Cucumber table.
20
- def create_records table
21
- table.map do |row|
22
- create_record row
23
- end
24
- end
25
-
26
-
27
- # Creates a new record in the database,
28
- # of the given class, with the given Cucumber-formatted String attributes.
29
- def create_record attributes
30
- FactoryGirl.create @clazz, factorygirl_row(symbolized_hash(attributes))
31
- end
32
-
33
-
34
- private
35
-
36
- # Returns the Cucumberator subclass to be used by this Cucumparer instance
37
- def self.cureator_class activerecord_class
38
- cureator_class_name(activerecord_class).constantize
39
- rescue NameError
40
- Cureator
41
- end
42
-
43
-
44
- # Returns the name of the Cucumberator subclass to be used by this Cucumparer instance.
45
- def self.cureator_class_name activerecord_class
46
- "#{activerecord_class.name}Cureator"
47
- end
48
-
49
-
50
- # Returns the given row, with values converted to FactoryGirl format
51
- #
52
- # Assumes the keys of the row are hashes
53
- def factorygirl_row row
54
- {}.tap do |result|
55
- row.each do |key, value|
56
- method = method_name key
57
- result[key] = respond_to?(method) ? send(method, value) : value
58
- end
59
- end
60
- end
61
-
62
-
63
- # Returns the name of the value_for method for the given key
64
- def method_name key
65
- "value_for_#{key}"
66
- end
67
-
68
-
69
- # Converts the key given in Cucumber format into FactoryGirl format
70
- def normalized_key key
71
- key.downcase.parameterize.underscore
72
- end
73
-
74
-
75
- # Returns a new hash with the keys normalized to symbols
76
- def symbolized_hash row
77
- {}.tap do |result|
78
- row.each do |key, value|
79
- result[normalized_key key] = value
80
- end
81
- end
82
- end
83
-
84
- end
85
-
86
- end