brutal 0.4.1 → 1.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 491dc26f34135ff61ceb54bc64b4dc5793e8b2610d545b3025b8c7aaf53b3064
4
- data.tar.gz: e7c379c45ce23f0886041ec8a4b6ab4c493a91e450230b64968e45a92214a62e
3
+ metadata.gz: 332c8755656f334781310460780dd233d621ddac83bc7d7dc5f0c555f4d1f955
4
+ data.tar.gz: c49ae44f98432fb3839bcf7c76ac3929bfee3eac82113bc4903e6d288863865e
5
5
  SHA512:
6
- metadata.gz: 8791614f90a92630a28aa3fb8b6d2a9d1921404b1cc614302bfeb8a87970186c33b935d81db81b7c1d8751475a59d5f32c17ea80627d32755daece880b93ace4
7
- data.tar.gz: 19cf4fcbe94766983442f0f47b2651b5b675d5ade1c9b306e4213421d21253035545a23f8e6c988a8967db182b4de59a4158a545278684b4104039729963574c
6
+ metadata.gz: f015f9cbc3f478c66374b72a3c6e7340863b472eac796833700aa11a501c3361b2519939f0e407308a6800df23bb7ba959ec60d70f5b16acb86f3d30ae0440e2
7
+ data.tar.gz: 90c7d4fef875977bd991a16405675e424524d2478318d33e694d073a47348986a2bb93bc27f9ff5cdfb5d5d48120d334f55733ad0ff6a3880c401ba901c85391
data/README.md CHANGED
@@ -8,29 +8,30 @@
8
8
 
9
9
  > A _code-first_ approach to automate the writing of unit tests.
10
10
 
11
- ## Why
11
+ ## Intro
12
12
 
13
13
  [![I Hate Tests](https://github.com/fixrb/brutal/raw/master/img/rubyhack-2019-ruby3-what-s-missing-by-yukihiro-matsumoto.jpg)](https://www.youtube.com/embed/cmOt9HhszCI?start=1732&end=1736 "I don't like tests. It's not DRY.")
14
14
 
15
15
  > I don't like tests. It's not DRY.<br/>
16
16
  > -- [Matz](https://github.com/matz)
17
17
 
18
- ## Purpose
18
+ ## Overview
19
19
 
20
- Let __Brutal__ shape for you the actual behavior of your code against as many combinations of challenges as needed.
20
+ Let __Brutal__ shape for you in no time the actual behavior of your code through as many combinations of contexts as needed.
21
21
 
22
- Without giving the power to test everything, it makes it easy to generate in no time a set of tests for all relevant contexts.
23
-
24
- By delegating to __Brutal__ this repetitive (and redundant) job of writing tests, you'll be able to focus on your core business: writing code.
22
+ By delegating to __Brutal__ the repetitive (and redundant) task of writing tests, you'll be able to focus on your core business: the code itself.
25
23
 
26
24
  ## Warning
27
25
 
28
- __Brutal__ does not prevent from bugs.
29
- As a picture of the behavior of the code, generated tests would be wrong if the code is wrong.
26
+ __Brutal__ development process does not prevent from bugs.
30
27
 
31
- This is why it is important to carefully read a generated test suite, to ensure that it describes the behavior of the code as it is expected to behave.
28
+ As a _picture of the behavior of the code_,
29
+ a generated test suite is wrong as long as the code is wrong,
30
+ regardless of whether all true expectations.
32
31
 
33
- This is the cost to enter the _Brutal-Driven Development_ with confidence.
32
+ However, this document becomes relevant when it shows that the code behaves as it is supposed to.
33
+ It is therefore important to read it well.
34
+ This is the price for _Brutal-Driven Development_.
34
35
 
35
36
  ## Installation
36
37
 
@@ -54,113 +55,63 @@ Just type `brutal` in a Ruby project's folder and watch the magic happen.
54
55
 
55
56
  ## Usage
56
57
 
57
- __Brutal__'s configuration file is `.brutal.yml`, which acts like a meta-spec.
58
- This YAML file can contains the following keys:
59
-
60
- * `header` (optional): Some code to execute before the test suite.
61
- * `front_object` (required): The front object of the test suite.
62
- * `subject` (required): The object of each context.
63
- * `variables` (required): A hash to generate the subject of each context.
64
- * `actual_values` (required): A list of tests to challenge the subject.
65
-
66
- ### Behavioral integrity
67
-
68
- In versioned projects,
69
- the integrity of the behavior of the code could easily be compared by executing `brutal` after changes.
70
-
71
- Assuming a project is versioned with git,
72
- if something goes wrong, the `git diff test.rb` command should instantly show changes between the behavior of the previous code and the behavior of the new one.
73
-
74
- Example of regression from [The Greeter class](https://github.com/fixrb/brutal/raw/master/examples/the_greeter_class/):
75
-
76
- ```diff
77
- require './greeter'
78
-
79
- # ------------------------------------------------------------------------------
80
-
81
- front_object = Greeter
82
-
83
- # ------------------------------------------------------------------------------
84
-
85
- actual = front_object.new('world')
86
-
87
- - raise unless actual.salute == "Hello World!"
88
- + raise unless actual.salute == "Hello !"
89
- ```
90
-
91
- ### Optional parameters
58
+ The `brutal.yml` file is a manifest you can use to define your __Brutal__ meta-spec.
59
+ It has 4 top-level sections:
92
60
 
93
- It would also be possible to ask for an RSpec template by passing "`rspec`" argument:
61
+ * `header` - Specifies the code to execute before generating the test suite.
62
+ * `subject` - Specifies the template of the code to be declined across contexts.
63
+ * `contexts` - Specifies a list of variables to populate the subject's template.
64
+ * `actuals` - Specifies templates to challenge evaluated subjects & get results.
94
65
 
95
- ```sh
96
- brutal rspec
97
- ```
98
-
99
- ## Example
66
+ ### Getting started
100
67
 
101
- Given this config file:
68
+ 1. Create a `brutal.yml` file in your application's root directory.
69
+ The following example `brutal.yml` defines the shape of a Hello test suite:
102
70
 
103
71
  ```yaml
104
72
  ---
105
- front_object: |
106
- "Hello "
107
-
108
73
  subject: |
109
- %{front_object} + %{string}
74
+ "Hello " + "%{string}"
110
75
 
111
- variables:
76
+ contexts:
112
77
  string:
113
- - "'Alice'"
114
- - "'Bob'"
78
+ - Alice
79
+ - Bob
115
80
 
116
- actual_values:
81
+ actuals:
117
82
  - "%{subject}.to_s"
118
83
  - "%{subject}.length"
119
84
  ```
120
85
 
121
- The `brutal` command would generate and write in to a `test.rb` file the following "Plain Old Ruby":
86
+ 2. Run the `brutal` command from the same directory.
87
+
88
+ 3. Read the generated `test.rb` file in the same directory:
122
89
 
123
90
  ```ruby
124
- front_object = "Hello "
91
+ # Brutal test suite
125
92
 
126
93
  # ------------------------------------------------------------------------------
127
94
 
128
- actual = front_object + 'Alice'
95
+ actual = begin
96
+ "Hello " + "Alice"
97
+ end
129
98
 
130
- raise unless actual.to_s == "Hello Alice"
131
- raise unless actual.length == 11
99
+ raise if actual.to_s != "Hello Alice"
100
+ raise if actual.length != 11
132
101
 
133
102
  # ------------------------------------------------------------------------------
134
103
 
135
- actual = front_object + 'Bob'
104
+ actual = begin
105
+ "Hello " + "Bob"
106
+ end
136
107
 
137
- raise unless actual.to_s == "Hello Bob"
138
- raise unless actual.length == 9
108
+ raise if actual.to_s != "Hello Bob"
109
+ raise if actual.length != 9
139
110
  ```
140
111
 
141
- And the `brutal rspec` command would generate and write in to a `test_spec.rb` file the following spec:
142
-
143
- ```ruby
144
- RSpec.describe do
145
- let(:front_object) { "Hello " }
146
-
147
- context do
148
- let(:actual) { front_object + 'Alice' }
149
-
150
- it { expect(actual.to_s).to eq("Hello Alice") }
151
- it { expect(actual.length).to eq(11) }
152
- end
153
-
154
- context do
155
- let(:actual) { front_object + 'Bob' }
156
-
157
- it { expect(actual.to_s).to eq("Hello Bob") }
158
- it { expect(actual.length).to eq(9) }
159
- end
160
- end
161
- ```
112
+ ### More examples
162
113
 
163
- More examples are available [here](https://github.com/fixrb/brutal/raw/master/examples/).
114
+ https://github.com/fixrb/brutal/raw/master/examples/
164
115
 
165
116
  ## Rake integration example
166
117
 
data/bin/brutal CHANGED
@@ -1,45 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'yaml'
4
+ require_relative File.join('..', 'lib', 'brutal')
5
5
 
6
- FILE_NAME = '.brutal.yml'
7
- CONF_PATH = ::File.join(::Dir.pwd, FILE_NAME)
8
-
9
- abort "File #{CONF_PATH} not found!" unless ::File.exist?(CONF_PATH)
10
-
11
- conf = ::YAML.load_file(CONF_PATH)
12
-
13
- header = conf.fetch('header', '').to_s.chomp
14
- front_object = conf.fetch('front_object')
15
- subject = conf.fetch('subject')
16
- variables = conf.fetch('variables', {})
17
- actual_values = conf.fetch('actual_values')
18
-
19
- raise ::TypeError unless subject.is_a?(::String)
20
- raise ::TypeError unless variables.is_a?(::Hash)
21
- raise ::TypeError unless actual_values.is_a?(::Array)
22
-
23
- front_object = front_object.chomp if front_object.is_a?(::String)
24
-
25
- require_relative ::File.join('..', 'lib', 'brutal')
26
-
27
- scaffold, file_name = case ARGV.fetch(0, nil)
28
- when nil
29
- [::Brutal::ScaffoldGenerator::Por, 'test.rb']
30
- when 'rspec'
31
- [::Brutal::ScaffoldGenerator::RSpec, 'test_spec.rb']
32
- else
33
- abort "#{ARGV[0].inspect} not (yet) supported!"
34
- end
35
-
36
- eval(header) # rubocop:disable Security/Eval
37
-
38
- doc = [
39
- header,
40
- scaffold.new(front_object, subject, *actual_values, **variables).to_s
41
- ].reject(&:empty?).join("\n\n")
42
-
43
- file = ::File.open(file_name, 'w')
44
- file.write(doc)
45
- file.close
6
+ Brutal.generate!
@@ -1,11 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Main namespace
4
- #
5
- # @api public
3
+ %w[
4
+ configuration
5
+ scaffold
6
+ ].each { |file_name| require_relative(File.join('brutal', file_name)) }
7
+
8
+ # The Brutal namespace
6
9
  module Brutal
7
- end
10
+ def self.settings
11
+ Configuration.load!
12
+ end
13
+
14
+ def self.generate
15
+ Scaffold.new(*settings)
16
+ end
17
+
18
+ def self.generate!
19
+ file = ::File.open('test.rb', 'w')
20
+ file.write(generate)
8
21
 
9
- Dir[File.join File.dirname(__FILE__), 'brutal', 'scaffold_generator', '*.rb'].each do |fname|
10
- require_relative fname
22
+ true
23
+ ensure
24
+ file.close
25
+ end
11
26
  end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ module Brutal
6
+ # Brutal::Configuration
7
+ #
8
+ # @since 1.0.0
9
+ class Configuration
10
+ NAME = '.brutal.yml'
11
+ PATH = ::File.join(::Dir.pwd, NAME).freeze
12
+
13
+ def self.load!
14
+ new.to_a
15
+ end
16
+
17
+ def self.file!
18
+ ::YAML.load_file(PATH)
19
+ rescue ::Errno::ENOENT => _e
20
+ abort("File #{PATH} not found!")
21
+ end
22
+
23
+ attr_reader(:header, :subject, :contexts, :actuals)
24
+
25
+ # rubocop:disable Metrics/AbcSize
26
+ def initialize
27
+ settings = self.class.file!
28
+
29
+ @header = settings.fetch('header', '# Brutal test suite')
30
+ @subject = settings.fetch('subject', '')
31
+ @contexts = settings.fetch('contexts', {})
32
+ @actuals = settings.fetch('actuals', [])
33
+
34
+ raise ::TypeError, @header.inspect unless @header.is_a?(::String)
35
+ raise ::TypeError, @subject.inspect unless @subject.is_a?(::String)
36
+ raise ::TypeError, @contexts.inspect unless @contexts.is_a?(::Hash)
37
+ raise ::TypeError, @actuals.inspect unless @actuals.is_a?(::Array)
38
+ end
39
+ # rubocop:enable Metrics/AbcSize
40
+
41
+ def to_a
42
+ [header, subject, *actuals, **contexts]
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Brutal
4
+ # Brutal::Scaffold
5
+ #
6
+ # @since 1.0.0
7
+ class Scaffold
8
+ attr_reader(:header, :subject, :actuals, :contexts)
9
+
10
+ # Initialize a new scaffold generator
11
+ def initialize(header, subject, *actuals, **contexts)
12
+ warn('Empty subject!') if subject.empty?
13
+ warn('Empty actual values!') if actuals.empty?
14
+ warn('Empty contexts!') if contexts.empty?
15
+
16
+ eval(header) # rubocop:disable Security/Eval
17
+
18
+ @header = header
19
+ @subject = subject
20
+ @actuals = actuals
21
+ @contexts = contexts
22
+ end
23
+
24
+ # Return a Ruby string that can be evaluated.
25
+ def inspect(object)
26
+ return object.to_s unless object.is_a?(::String)
27
+
28
+ object.strip
29
+ end
30
+
31
+ # Return a string representation
32
+ #
33
+ # @return [String]
34
+ #
35
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
36
+ def to_s
37
+ header.chomp + "\n" + blank_line + combinations_values.map do |values|
38
+ attributes = context_names.each_with_index.inject({}) do |h, (name, i)|
39
+ h.merge(name.to_sym => inspect(values.fetch(i)))
40
+ end
41
+
42
+ actual_str = format(inspect(subject), **attributes)
43
+
44
+ string = <<~CODE
45
+ actual = begin
46
+ #{actual_str.gsub(/^/, ' ')}
47
+ end
48
+
49
+ CODE
50
+
51
+ actual = eval(actual_str) # rubocop:disable Security/Eval, Lint/UselessAssignment
52
+
53
+ actuals.each do |actual_value|
54
+ result_str = format(actual_value, subject: 'actual')
55
+ string += "raise if #{result_str} != #{eval(result_str).inspect}\n" # rubocop:disable Security/Eval
56
+ end
57
+
58
+ string
59
+ end.join(blank_line)
60
+ end
61
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
62
+
63
+ def blank_line
64
+ "\n" \
65
+ "# #{('-' * 78)}\n" \
66
+ "\n"
67
+ end
68
+
69
+ def context_names
70
+ contexts.keys.sort
71
+ end
72
+
73
+ def contexts_values
74
+ context_names.map { |context_name| contexts.fetch(context_name) }
75
+ end
76
+
77
+ def combinations_values
78
+ Array(contexts_values[0]).product(*Array(contexts_values[1..-1]))
79
+ end
80
+ end
81
+ end
metadata CHANGED
@@ -1,113 +1,99 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brutal
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cyril Kato
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-23 00:00:00.000000000 Z
11
+ date: 2020-03-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '2.1'
19
+ version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '2.1'
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '13.0'
33
+ version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '13.0'
41
- - !ruby/object:Gem::Dependency
42
- name: rubocop
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: '0.79'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - "~>"
53
- - !ruby/object:Gem::Version
54
- version: '0.79'
40
+ version: '0'
55
41
  - !ruby/object:Gem::Dependency
56
42
  name: rubocop-performance
57
43
  requirement: !ruby/object:Gem::Requirement
58
44
  requirements:
59
- - - "~>"
45
+ - - ">="
60
46
  - !ruby/object:Gem::Version
61
- version: '1.5'
47
+ version: '0'
62
48
  type: :development
63
49
  prerelease: false
64
50
  version_requirements: !ruby/object:Gem::Requirement
65
51
  requirements:
66
- - - "~>"
52
+ - - ">="
67
53
  - !ruby/object:Gem::Version
68
- version: '1.5'
54
+ version: '0'
69
55
  - !ruby/object:Gem::Dependency
70
- name: simplecov
56
+ name: rubocop-thread_safety
71
57
  requirement: !ruby/object:Gem::Requirement
72
58
  requirements:
73
- - - "~>"
59
+ - - ">="
74
60
  - !ruby/object:Gem::Version
75
- version: '0.17'
61
+ version: '0'
76
62
  type: :development
77
63
  prerelease: false
78
64
  version_requirements: !ruby/object:Gem::Requirement
79
65
  requirements:
80
- - - "~>"
66
+ - - ">="
81
67
  - !ruby/object:Gem::Version
82
- version: '0.17'
68
+ version: '0'
83
69
  - !ruby/object:Gem::Dependency
84
- name: spectus
70
+ name: simplecov
85
71
  requirement: !ruby/object:Gem::Requirement
86
72
  requirements:
87
- - - "~>"
73
+ - - ">="
88
74
  - !ruby/object:Gem::Version
89
- version: '3.1'
75
+ version: '0'
90
76
  type: :development
91
77
  prerelease: false
92
78
  version_requirements: !ruby/object:Gem::Requirement
93
79
  requirements:
94
- - - "~>"
80
+ - - ">="
95
81
  - !ruby/object:Gem::Version
96
- version: '3.1'
82
+ version: '0'
97
83
  - !ruby/object:Gem::Dependency
98
84
  name: yard
99
85
  requirement: !ruby/object:Gem::Requirement
100
86
  requirements:
101
- - - "~>"
87
+ - - ">="
102
88
  - !ruby/object:Gem::Version
103
- version: '0.9'
89
+ version: '0'
104
90
  type: :development
105
91
  prerelease: false
106
92
  version_requirements: !ruby/object:Gem::Requirement
107
93
  requirements:
108
- - - "~>"
94
+ - - ">="
109
95
  - !ruby/object:Gem::Version
110
- version: '0.9'
96
+ version: '0'
111
97
  description: A code-first approach to automate the writing of unit tests.
112
98
  email: contact@cyril.email
113
99
  executables:
@@ -119,9 +105,8 @@ files:
119
105
  - README.md
120
106
  - bin/brutal
121
107
  - lib/brutal.rb
122
- - lib/brutal/scaffold_generator/base.rb
123
- - lib/brutal/scaffold_generator/por.rb
124
- - lib/brutal/scaffold_generator/rspec.rb
108
+ - lib/brutal/configuration.rb
109
+ - lib/brutal/scaffold.rb
125
110
  homepage: https://github.com/fixrb/brutal
126
111
  licenses:
127
112
  - MIT
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Brutal
4
- module ScaffoldGenerator
5
- # Base class
6
- class Base
7
- # @api private
8
- attr_reader :front_object_str, :front_object, :subject, :actual_values, :variables
9
-
10
- # Initialize a new scaffold generator
11
- def initialize(front_object, subject, *actual_values, **variables)
12
- raise 'Empty actual values!' if actual_values.empty?
13
- raise 'Empty variables!' if variables.empty?
14
-
15
- @front_object_str = inspect(front_object)
16
- @front_object = eval(@front_object_str) # rubocop:disable Security/Eval
17
- @subject = subject
18
- @actual_values = actual_values
19
- @variables = variables
20
- end
21
-
22
- # Return a Ruby string that can be evaluated.
23
- def inspect(object)
24
- return object.to_s unless object.is_a?(::String)
25
-
26
- object.strip
27
- end
28
- end
29
- end
30
- end
@@ -1,58 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'base'
4
-
5
- module Brutal
6
- module ScaffoldGenerator
7
- # Plain Old Ruby.
8
- #
9
- # @api private
10
- #
11
- class Por < Base
12
- # Return a string representation
13
- #
14
- # @return [String]
15
- #
16
- # @api public
17
- def to_s
18
- names = variables.keys.sort
19
- values_arr = names.map { |name| variables.fetch(name) }
20
-
21
- test_params = Array(values_arr[0]).product(*Array(values_arr[1..-1]))
22
-
23
- blank_line.gsub(/\A\n/, '') +
24
- "front_object = #{front_object_str}\n" +
25
- blank_line +
26
- test_params.map do |values|
27
- attributes = names.each_with_index.inject(front_object: 'front_object') do |h, (name, i)|
28
- h.merge(name.to_sym => inspect(values.fetch(i)))
29
- end
30
-
31
- actual_str = format(inspect(subject), **attributes)
32
-
33
- string = <<~CODE
34
- actual = begin
35
- #{actual_str.gsub(/^/, ' ')}
36
- end
37
-
38
- CODE
39
-
40
- actual = eval(actual_str) # rubocop:disable Security/Eval, Lint/UselessAssignment
41
-
42
- actual_values.each do |actual_value|
43
- result_str = format(actual_value, subject: 'actual')
44
- string += "raise unless #{result_str} == #{eval(result_str).inspect}\n" # rubocop:disable Security/Eval
45
- end
46
-
47
- string
48
- end.join(blank_line)
49
- end
50
-
51
- def blank_line
52
- "\n" \
53
- "# #{('-' * 78)}\n" \
54
- "\n"
55
- end
56
- end
57
- end
58
- end
@@ -1,55 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'base'
4
-
5
- module Brutal
6
- module ScaffoldGenerator
7
- # Plain Old Ruby.
8
- #
9
- # @api private
10
- #
11
- class RSpec < Base
12
- # Return a string representation
13
- #
14
- # @return [String]
15
- #
16
- # @api public
17
- def to_s
18
- names = variables.keys.sort
19
- values_arr = names.map { |name| variables.fetch(name) }
20
-
21
- test_params = Array(values_arr[0]).product(*Array(values_arr[1..-1]))
22
-
23
- "RSpec.describe do\n" \
24
- " let(:front_object) { #{front_object_str} }\n" \
25
- "\n" +
26
- test_params.map do |values|
27
- string = " context do\n"
28
-
29
- attributes = names.each_with_index.inject(front_object: 'front_object') do |h, (name, i)|
30
- h.merge(name.to_sym => inspect(values.fetch(i)))
31
- end
32
-
33
- actual_str = format(inspect(subject), **attributes)
34
-
35
- string += " let(:actual) { #{actual_str} }\n\n"
36
- actual = eval(actual_str) # rubocop:disable Security/Eval, Lint/UselessAssignment
37
-
38
- actual_values.each do |actual_value|
39
- result_str = format(actual_value, subject: 'actual')
40
- string += " it { expect(#{result_str}).to eq(#{eval(result_str).inspect}) }\n" # rubocop:disable Security/Eval
41
- end
42
-
43
- string += " end\n"
44
-
45
- string
46
- end.join(blank_line) +
47
- "end\n"
48
- end
49
-
50
- def blank_line
51
- "\n"
52
- end
53
- end
54
- end
55
- end