rspec-varys 0.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: c9b5d6746ad0ced69ace33d7f785783a1a9289c3
4
- data.tar.gz: e5579d289843714febeddd99b882d95f79c94779
2
+ SHA256:
3
+ metadata.gz: 84ad5d3575e590204e17a0dc39078c65bb0305b37bf817119fd754b2535dd504
4
+ data.tar.gz: d63f46eaa21e08baeafb492d02f710e23ed9a8a4064f24ccb6a832c5af50cb4a
5
5
  SHA512:
6
- metadata.gz: 4b25c426e5f447bb33d3d2216c0b593d0d1be37ab1be19223217b706a4a54a1d8dd796e779fea428c487a34443086e2da95178ae78e1d6613ea1dfb60ab99ddc
7
- data.tar.gz: e485f28b72288b5ecf2212e1c3f7bcfd115a32918838f6a8a1e9ac93ed6ae4046a3cd0c7a07b9de7ed15cfc9b22e2d53b96f9b9e99645a4fbb9a28e727b58c3d
6
+ metadata.gz: 88d00f04f6bfed58a6937e26648f13f467efbf1ad7492bf06895419ef7607586b39ec6f7b05c81c40e14afd634f21493a41cf3095b176290f85add0257b1b589
7
+ data.tar.gz: f1308ced4b06f2def0f3c733cb6e707c58d477352a32c82fd7b007ac552f1b5b80492f62816b3fc3e178fbd3f98488dbfd340a32c405947add4d21cc50ff0d16
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.1.2
1
+ 3.0.1
data/.tool-versions ADDED
@@ -0,0 +1 @@
1
+ ruby 3.0.1
data/.travis.yml ADDED
@@ -0,0 +1,17 @@
1
+ language: ruby
2
+ bundler_args:
3
+ rvm:
4
+ - 2.1.2
5
+
6
+ addons:
7
+ code_climate:
8
+ repo_token: dbd468edcfe120e56aec92ebf99ff6c030becc1910a8d1684102c6fa2e907439
9
+
10
+ deploy:
11
+ provider: rubygems
12
+ api_key:
13
+ secure: WLQzsGZ6aPzqsyS1Mu4YCZjer4ZXRFQo5j1XPkTdR/bYmip2GBQAoVIUihlIXrcTSDKRk4VVZ62GkVSOkTA41vAVvD69ARGNIgwSDzLfoVp7RWEIi7peUBdYhzOoQM8eFnOKa3UeZbEqjolsTjtQfMQISNCeL9TCmdRcyLa/avk=
14
+ gem: rspec-varys
15
+ on:
16
+ tags: true
17
+ repo: ritchiey/rspec-varys
data/Gemfile CHANGED
@@ -1,5 +1,4 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
-
5
- gem 'pry-rescue'
4
+ gem "codeclimate-test-reporter", group: :test, require: nil
data/README.md CHANGED
@@ -1,10 +1,56 @@
1
- [![Gem Version](https://badge.fury.io/rb/rspec-varys.svg)](http://badge.fury.io/rb/rspec-varys)
1
+ [![Gem Version](https://badge.fury.io/rb/rspec-varys.svg)](http://badge.fury.io/rb/rspec-varys) [![Build Status](https://travis-ci.org/ritchiey/rspec-varys.svg?branch=master)](https://travis-ci.org/ritchiey/rspec-varys) [![Code Climate](https://codeclimate.com/github/ritchiey/rspec-varys/badges/gpa.svg)](https://codeclimate.com/github/ritchiey/rspec-varys)
2
2
 
3
3
  # Rspec::Varys
4
4
 
5
- Generate RSpec specs from intelligence gathered from doubles and spies.
5
+ Generate RSpec specs from intelligence gathered from doubles and spies. This is an experiment to see if a top-down TDD work-flow can be improved by partially automating the creation of lower level specs.
6
6
 
7
- This is an experiment to see if a top-down TDD work-flow can be improved by partially automating the creation of lower level specs.
7
+ When you define a test-double in your spec:
8
+
9
+ ```ruby
10
+ describe ExampleClass, "#max_margin_for" do
11
+ it "returns the maximum margin for the supplied text" do
12
+ allow(subject).to receive(:margin_for).and_return(2, 4)
13
+ expect(subject.max_margin_for(" unindent line\n indented line\n")).to eq(4)
14
+ end
15
+ end
16
+ ```
17
+
18
+ And your code calls it:
19
+
20
+ ```ruby
21
+ class ExampleClass
22
+ def max_margin_for(text)
23
+ text.split("\n").map {|line| margin_for(line)}.max
24
+ end
25
+ end
26
+ ```
27
+
28
+
29
+ Varys will generate the corresponding specs so you can verify the validity of your test-doubles:
30
+
31
+ ```ruby
32
+ # Generated by Varys:
33
+
34
+ describe ExampleClass, "#margin_for" do
35
+ it "returns something" do
36
+ confirm(subject).can receive(:margin_for).with(" unindent line").and_return(2)
37
+ skip "remove this line once implemented"
38
+ expect(subject.margin_for(" unindent line")).to eq(2)
39
+ end
40
+ end
41
+
42
+ describe ExampleClass, "#margin_for" do
43
+ it "returns something" do
44
+ confirm(subject).can receive(:margin_for).with(" indented line").and_return(4)
45
+ skip "remove this line once implemented"
46
+ expect(subject.margin_for(" indented line")).to eq(4)
47
+ end
48
+ end
49
+ ```
50
+
51
+ ## Limitations
52
+
53
+ Varys is very early release software and it has many limitations. If you find one, please check the Github issues and add it if it's not already listed.
8
54
 
9
55
  ## Installation
10
56
 
@@ -20,9 +66,35 @@ Or install it yourself as:
20
66
 
21
67
  $ gem install rspec-varys
22
68
 
69
+ ## Configuration
70
+
71
+ Add these lines to your `spec/spec_helper.rb`:
72
+
73
+ ```ruby
74
+ require "rspec/varys"
75
+ require "rspec/varys/rspec_generator"
76
+
77
+ RSpec.configure do |config|
78
+ config.include RSpec::Varys::DSL
79
+
80
+ config.before(:suite) do
81
+ RSpec::Varys.reset
82
+ end
83
+
84
+ config.after(:suite) do
85
+ # Required: create varys.yml
86
+ RSpec::Varys.print_report
87
+
88
+ # Optional: create generated_specs.yml from varys.yml
89
+ RSpec::Varys::RSpecGenerator.run
90
+ end
91
+ end
92
+ ```
93
+
23
94
  ## Usage
24
95
 
25
- See the [Cucumber features](https://relishapp.com/spechero/rspec-varys/docs) for examples of intended usage.
96
+ See the [Cucumber features](https://relishapp.com/spechero/rspec-varys/docs) for examples of intended usage or watch [this screencast](https://vimeo.com/119725799) for a simple tutorial.
97
+
26
98
 
27
99
  ## Contributing
28
100
 
data/Rakefile CHANGED
@@ -1,2 +1,14 @@
1
1
  require "bundler/gem_tasks"
2
2
 
3
+ require 'rubygems'
4
+ require 'cucumber'
5
+ require 'cucumber/rake/task'
6
+ require 'rspec/core/rake_task'
7
+
8
+
9
+ Cucumber::Rake::Task.new(:features) do |t|
10
+ t.cucumber_opts = "features --format pretty"
11
+ end
12
+
13
+ task :default => [:spec, :features]
14
+ RSpec::Core::RakeTask.new
data/bin/genspecs ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env ruby
2
+
@@ -0,0 +1 @@
1
+ default: --publish-quiet
@@ -6,6 +6,7 @@ Feature: Generating an RSpec Spec from an RSpec Expectation
6
6
  $:.unshift File.expand_path('../../lib', File.dirname(__FILE__))
7
7
 
8
8
  require "rspec/varys"
9
+ require "rspec/varys/rspec_generator"
9
10
 
10
11
  RSpec.configure do |config|
11
12
 
@@ -17,11 +18,138 @@ Feature: Generating an RSpec Spec from an RSpec Expectation
17
18
 
18
19
  config.after(:suite) do
19
20
  RSpec::Varys.print_report
21
+ RSpec::Varys::RSpecGenerator.run
20
22
  end
21
23
  end
22
24
  """
23
25
 
24
26
 
27
+ Scenario: For a test double
28
+ Given a file named "top_level_spec.rb" with:
29
+ """ruby
30
+ require_relative 'spec_helper'
31
+ require_relative 'person'
32
+
33
+ describe "First day at work" do
34
+
35
+ it "starts with an introduction" do
36
+ name = double('Name', full_name: "Dick Jones")
37
+ boss = Person.new(name)
38
+ expect(boss.welcome).to eq "Welcome to OCP, I'm Dick Jones"
39
+ end
40
+
41
+ end
42
+ """
43
+ And a file named "person.rb" with:
44
+ """ruby
45
+ class Person
46
+
47
+ def initialize(name)
48
+ @name = name
49
+ end
50
+
51
+ def welcome
52
+ "Welcome to OCP, I'm #{@name.full_name}"
53
+ end
54
+
55
+ end
56
+ """
57
+
58
+ When I run `rspec top_level_spec.rb`
59
+ Then it should pass with:
60
+ """ruby
61
+ Specs have been generated based on mocks you aren't currently testing.
62
+ """
63
+ And the file "varys.yaml" should contain:
64
+ """yaml
65
+ ---
66
+ :untested_stubs:
67
+ - :class_name: Name
68
+ :type: instance
69
+ :method: full_name
70
+ :returns: Dick Jones
71
+ """
72
+
73
+ And the file "generated_specs.rb" should contain:
74
+ """ruby
75
+ describe Name, "#full_name" do
76
+
77
+ it "returns something" do
78
+ confirm(subject).can receive(:full_name).and_return("Dick Jones")
79
+ skip "remove this line once implemented"
80
+ expect(subject.full_name).to eq("Dick Jones")
81
+ end
82
+
83
+ end
84
+
85
+
86
+ """
87
+ Scenario: For a class method
88
+ Given a file named "top_level_spec.rb" with:
89
+ """ruby
90
+ require_relative 'spec_helper'
91
+ require_relative 'person'
92
+
93
+ describe "First day at work" do
94
+
95
+ it "starts with an introduction" do
96
+ boss = Person.new('Dick', 'Jones')
97
+ allow(Person).to receive(:full_name).with("Dick", "Jones").and_return("Dick Jones")
98
+ expect(boss.welcome).to eq "Welcome to OCP, I'm Dick Jones"
99
+ end
100
+
101
+ end
102
+ """
103
+ And a file named "person.rb" with:
104
+ """ruby
105
+ class Person
106
+
107
+ def initialize(firstname, lastname)
108
+ @firstname = firstname
109
+ @lastname = lastname
110
+ end
111
+
112
+ def welcome
113
+ "Welcome to OCP, I'm #{Person.full_name(@firstname, @lastname)}"
114
+ end
115
+
116
+ end
117
+ """
118
+
119
+ When I run `rspec top_level_spec.rb`
120
+ Then it should pass with:
121
+ """ruby
122
+ Specs have been generated based on mocks you aren't currently testing.
123
+ """
124
+ And the file "varys.yaml" should contain:
125
+ """yaml
126
+ ---
127
+ :untested_stubs:
128
+ - :class_name: Person
129
+ :type: class
130
+ :method: full_name
131
+ :returns: Dick Jones
132
+ :arguments:
133
+ - Dick
134
+ - Jones
135
+ """
136
+
137
+ And the file "generated_specs.rb" should contain:
138
+ """ruby
139
+ describe Person, ".full_name" do
140
+
141
+ it "returns something" do
142
+ confirm(described_class).can receive(:full_name).with("Dick", "Jones").and_return("Dick Jones")
143
+ skip "remove this line once implemented"
144
+ expect(described_class.full_name("Dick", "Jones")).to eq("Dick Jones")
145
+ end
146
+
147
+ end
148
+
149
+
150
+ """
151
+
152
+
25
153
  Scenario: For a single unmatched expectation
26
154
  Given a file named "top_level_spec.rb" with:
27
155
  """ruby
@@ -54,24 +182,32 @@ Feature: Generating an RSpec Spec from an RSpec Expectation
54
182
 
55
183
  When I run `rspec top_level_spec.rb`
56
184
  Then it should pass with:
57
- """
185
+ """ruby
58
186
  Specs have been generated based on mocks you aren't currently testing.
59
187
  """
60
- And the file "generated_specs/person_spec.rb" should contain:
188
+ And the file "varys.yaml" should contain:
189
+ """yaml
190
+ ---
191
+ :untested_stubs:
192
+ - :class_name: Person
193
+ :type: instance
194
+ :method: full_name
195
+ :returns: Dick Jones
61
196
  """
62
- describe Person do
63
197
 
64
- describe "#full_name" do
65
-
66
- it "returns the correct value" do
67
- pending
68
- confirm(subject).can receive(:full_name).and_return("Dick Jones")
69
- expect(subject.full_name).to eq("Dick Jones")
70
- end
198
+ And the file "generated_specs.rb" should contain:
199
+ """ruby
200
+ describe Person, "#full_name" do
71
201
 
202
+ it "returns something" do
203
+ confirm(subject).can receive(:full_name).and_return("Dick Jones")
204
+ skip "remove this line once implemented"
205
+ expect(subject.full_name).to eq("Dick Jones")
72
206
  end
73
207
 
74
208
  end
209
+
210
+
75
211
  """
76
212
 
77
213
 
@@ -108,36 +244,50 @@ Feature: Generating an RSpec Spec from an RSpec Expectation
108
244
 
109
245
  When I run `rspec top_level_spec.rb`
110
246
  Then it should pass with:
111
- """
247
+ """ruby
112
248
  Specs have been generated based on mocks you aren't currently testing.
113
249
  """
114
- And the file "generated_specs/person_spec.rb" should contain:
250
+ And the file "varys.yaml" should contain:
251
+ """yaml
252
+ ---
253
+ :untested_stubs:
254
+ - :class_name: Person
255
+ :type: instance
256
+ :method: title
257
+ :returns: Vice President
258
+ - :class_name: Person
259
+ :type: instance
260
+ :method: full_name
261
+ :returns: Dick Jones
115
262
  """
116
- describe Person do
117
-
118
- describe "#title" do
119
263
 
120
- it "returns the correct value" do
121
- pending
122
- confirm(subject).can receive(:title).and_return("Vice President")
123
- expect(subject.title).to eq("Vice President")
124
- end
264
+ And the file "generated_specs.rb" should contain:
265
+ """ruby
266
+ describe Person, "#title" do
125
267
 
268
+ it "returns something" do
269
+ confirm(subject).can receive(:title).and_return("Vice President")
270
+ skip "remove this line once implemented"
271
+ expect(subject.title).to eq("Vice President")
126
272
  end
127
273
 
128
- describe "#full_name" do
274
+ end
129
275
 
130
- it "returns the correct value" do
131
- pending
132
- confirm(subject).can receive(:full_name).and_return("Dick Jones")
133
- expect(subject.full_name).to eq("Dick Jones")
134
- end
135
276
 
277
+ describe Person, "#full_name" do
278
+
279
+ it "returns something" do
280
+ confirm(subject).can receive(:full_name).and_return("Dick Jones")
281
+ skip "remove this line once implemented"
282
+ expect(subject.full_name).to eq("Dick Jones")
136
283
  end
137
284
 
138
285
  end
286
+
287
+
139
288
  """
140
289
 
290
+
141
291
  Scenario: For one matched and one unmatched expectation
142
292
  Given a file named "top_level_spec.rb" with:
143
293
  """ruby
@@ -148,8 +298,8 @@ Feature: Generating an RSpec Spec from an RSpec Expectation
148
298
 
149
299
  it "starts with an introduction" do
150
300
  boss = Person.new('Dick', 'Jones')
151
- expect(boss).to receive(:full_name).and_return("Dick Jones")
152
- expect(boss).to receive(:title).and_return("Vice President")
301
+ allow(boss).to receive(:full_name).and_return("Dick Jones")
302
+ allow(boss).to receive(:title).and_return("Vice President")
153
303
  expect(boss.welcome).to eq "Welcome to OCP, I'm Vice President Dick Jones"
154
304
  end
155
305
 
@@ -162,7 +312,6 @@ Feature: Generating an RSpec Spec from an RSpec Expectation
162
312
  describe "#full_name" do
163
313
 
164
314
  it "returns the correct value" do
165
- pending
166
315
  confirm(subject).can receive(:full_name).and_return("Dick Jones")
167
316
  # ...
168
317
  end
@@ -188,24 +337,17 @@ Feature: Generating an RSpec Spec from an RSpec Expectation
188
337
 
189
338
  When I run `rspec top_level_spec.rb`
190
339
  Then it should pass with:
191
- """
340
+ """ruby
192
341
  Specs have been generated based on mocks you aren't currently testing.
193
342
  """
194
- And the file "generated_specs/person_spec.rb" should contain:
195
- """
196
- describe Person do
197
-
198
- describe "#title" do
199
-
200
- it "returns the correct value" do
201
- pending
202
- confirm(subject).can receive(:title).and_return("Vice President")
203
- expect(subject.title).to eq("Vice President")
204
- end
205
-
206
- end
207
-
208
- end
343
+ And the file "varys.yaml" should contain:
344
+ """yaml
345
+ ---
346
+ :untested_stubs:
347
+ - :class_name: Person
348
+ :type: instance
349
+ :method: title
350
+ :returns: Vice President
209
351
  """
210
352
 
211
353
  Scenario: For an expectation with parameters
@@ -221,8 +363,6 @@ Feature: Generating an RSpec Spec from an RSpec Expectation
221
363
  describe "#full_name" do
222
364
 
223
365
  it "returns the correct value" do
224
- pending
225
- confirm(subject).can receive(:full_name).and_return("Dick Jones")
226
366
  expect(subject).to receive(:join_names).with("Dick", "Jones").and_return("Dick Jones")
227
367
  subject.full_name
228
368
  end
@@ -254,22 +394,37 @@ Feature: Generating an RSpec Spec from an RSpec Expectation
254
394
 
255
395
  When I run `rspec top_level_spec.rb`
256
396
  Then it should pass with:
257
- """
397
+ """ruby
258
398
  Specs have been generated based on mocks you aren't currently testing.
259
399
  """
260
- And the file "generated_specs/person_spec.rb" should contain:
400
+ And the file "varys.yaml" should contain:
401
+ """yaml
402
+ ---
403
+ :untested_stubs:
404
+ - :class_name: Person
405
+ :type: instance
406
+ :method: join_names
407
+ :returns: Dick Jones
408
+ :arguments:
409
+ - Dick
410
+ - Jones
261
411
  """
262
- describe Person do
263
412
 
264
- describe "#join_names" do
265
-
266
- it "returns the correct value" do
267
- pending
268
- confirm(subject).can receive(:join_names).with("Dick", "Jones").and_return("Dick Jones")
269
- expect(subject.join_names("Dick", "Jones")).to eq("Dick Jones")
270
- end
413
+ And the file "generated_specs.rb" should contain:
414
+ """ruby
415
+ describe Person, "#join_names" do
271
416
 
417
+ it "returns something" do
418
+ confirm(subject).can receive(:join_names).with("Dick", "Jones").and_return("Dick Jones")
419
+ skip "remove this line once implemented"
420
+ expect(subject.join_names("Dick", "Jones")).to eq("Dick Jones")
272
421
  end
273
422
 
274
423
  end
424
+
425
+
275
426
  """
427
+
428
+
429
+
430
+
data/features/readme.md CHANGED
@@ -1,4 +1,4 @@
1
- *Everything should be built top-down, except the first time - Alan
1
+ *Everything should be built top-down, except the first time 1. Alan
2
2
  Perlis*
3
3
 
4
4
  RSpec-Varys automatically generates RSpec specs from your mocked methods each time your suite is run.
@@ -7,4 +7,27 @@ This enables you to build your program from the top-down (or outside-in if
7
7
  your prefer) without having to manually keep track of which mocks have
8
8
  been validated.
9
9
 
10
- Available on [Github](https://github.com/ritchiey/rspec-varys)
10
+ Installation instructions can be found [here](https://github.com/ritchiey/rspec-varys).
11
+
12
+ A typical workflow using rspec-varys looks like this. Note, I haven't
13
+ written "and run your specs again" at the end of each step, that's
14
+ implied.
15
+
16
+ - if you have no failing specs and you need some new functionality, write a new top-level spec.
17
+
18
+ - if your specs are failing because your **spec** calls a non-existent function,
19
+ write the code for that function. Feel free to call methods that
20
+ don't exist yet.
21
+
22
+ - if your specs are failing because your **code** calls a non-existent function
23
+ stub that function out in the spec using `allow`.
24
+
25
+ - if your specs pass but varys generates new specs for untested stubs,
26
+ copy the generated specs into the appropriate spec file.
27
+
28
+ - if varys warns you about unneeded specs, delete those specs and any
29
+ code that can be removed without making other specs fail.
30
+
31
+ - if your specs pass but there are pending specs, pick one and remove
32
+ the `pending` statement.
33
+
@@ -1 +1,3 @@
1
1
  require 'aruba/cucumber'
2
+
3
+
data/lib/rspec/varys.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require "rspec/varys/version"
2
2
  require "fileutils"
3
+ require 'yaml'
3
4
 
4
5
  module RSpec
5
6
  module Varys
@@ -26,11 +27,25 @@ module RSpec
26
27
  {
27
28
  class_name: class_name,
28
29
  message: message,
29
- args: [],
30
- return_value: "Dick Jones"
30
+ args: args,
31
+ return_value: return_value
31
32
  }
32
33
  end
33
34
 
35
+ def args
36
+ customization = customizations.find{|c| c.instance_variable_get('@method_name') == :with}
37
+ (customization && customization.instance_variable_get('@args')) || []
38
+ end
39
+
40
+ def return_value
41
+ customization = customizations.find{|c| c.instance_variable_get('@method_name') == :and_return}
42
+ customization && customization.instance_variable_get('@args').first
43
+ end
44
+
45
+ def customizations
46
+ @ability.instance_variable_get('@recorded_customizations')
47
+ end
48
+
34
49
  def class_name
35
50
  @object.class.name
36
51
  end
@@ -68,10 +83,6 @@ module RSpec::Varys
68
83
  @recorded_messages
69
84
  end
70
85
 
71
- def self.generated_specs
72
- @generated_specs ||= generate_specs
73
- end
74
-
75
86
  def self.reset
76
87
  @recorded_messages = []
77
88
  @generated_specs = nil
@@ -80,96 +91,61 @@ module RSpec::Varys
80
91
 
81
92
  def self.record(object, message, args, block, return_value)
82
93
  @recorded_messages << {
83
- class_name: object.class.name,
94
+ class_name: class_name_for(object),
95
+ type: type_for(object),
84
96
  message: message,
85
97
  args: args,
86
98
  return_value: return_value
87
99
  }
88
100
  end
89
101
 
90
- def self.generate_specs
91
- {}.tap do |generated_specs|
92
- unconfirmed_messages.each do |s|
93
- generated_specs[s[:class_name]] ||= []
94
- generated_specs[s[:class_name]] << generate_spec(s)
95
- end
96
- end
97
- end
98
-
99
-
100
- def self.generate_spec(s)
101
- <<-GENERATED
102
- describe "##{s[:message]}" do
103
-
104
- it "returns the correct value" do
105
- pending
106
- confirm(subject).can receive(:#{s[:message]})#{with_parameters(s)}.and_return(#{serialize s[:return_value]})
107
- expect(subject.#{s[:message]}#{parameters(s)}).to eq(#{serialize s[:return_value]})
108
- end
109
-
110
- end
111
-
112
- GENERATED
102
+ def self.type_for(object)
103
+ object.kind_of?(Class) ? 'class' : 'instance'
113
104
  end
114
105
 
115
- def self.with_parameters(spec)
116
- if spec[:args].length > 0
117
- %Q[.with(#{params spec[:args]})]
106
+ def self.class_name_for(object)
107
+ if object.kind_of? RSpec::Mocks::Double
108
+ 'Name'
118
109
  else
119
- ""
110
+ object.kind_of?(Class) ? object.name : object.class.name
120
111
  end
121
112
  end
122
113
 
123
- def self.params(args)
124
- args.map{|a| serialize(a)}.join(", ")
125
- end
126
-
127
- def self.parameters(spec)
128
- if spec[:args].length > 0
129
- %Q[(#{params spec[:args]})]
130
- else
131
- ""
132
- end
133
- end
134
114
 
135
115
  def self.print_report
136
- dest_path = "generated_specs"
137
- FileUtils.mkdir_p dest_path
138
- generated_specs.each do |class_name, specs|
139
- File.open("#{dest_path}/#{underscore class_name}_spec.rb", 'w') do |file|
140
- file.write "describe #{class_name} do\n\n"
141
- specs.each do |spec|
142
- file.write(spec)
143
- end
144
- file.write "end"
145
- end
116
+ open_yaml_file do |yaml_file|
117
+ yaml_file.write YAML.dump(report)
146
118
  end
147
119
  puts "Specs have been generated based on mocks you aren't currently testing."
148
120
  end
149
121
 
150
- def self.unconfirmed_messages
151
- recorded_messages - confirmed_messages
122
+ def self.report
123
+ {
124
+ untested_stubs: unconfirmed_messages.map do |call|
125
+ {
126
+ class_name: call[:class_name],
127
+ type: call[:type],
128
+ method: call[:message].to_s,
129
+ returns: call[:return_value]
130
+ }.merge(arguments_if_any(call))
131
+ end
132
+ }
152
133
  end
153
134
 
154
- def self.underscore(camel_cased_word)
155
- camel_cased_word.downcase
135
+ def self.arguments_if_any(call)
136
+ call[:args].length > 0 ? { arguments: call[:args] } : { }
156
137
  end
157
138
 
158
- # Attempt to recreate the source-code to represent this argument in the setup
159
- # for our generated spec.
160
- def self.serialize(arg)
161
- if %w(Array Hash Float Fixnum String).include? arg.class.name
162
- arg.pretty_inspect.chop
163
- else
164
- guess_constructor arg
139
+ def self.open_yaml_file
140
+ File.open("varys.yaml", 'w') do |io|
141
+ yield io
165
142
  end
166
143
  end
167
144
 
168
- # Don't recognise the type so we don't know how to recreate it
169
- # in source code. So we'll take a guess at what might work and
170
- # let the user fix it up if necessary.
171
- def self.guess_constructor(arg)
172
- "#{arg.class.name}.new(#{serialize(arg.to_s)})"
145
+
146
+ def self.unconfirmed_messages
147
+ recorded_messages - confirmed_messages
173
148
  end
149
+
174
150
  end
175
151
 
@@ -0,0 +1,67 @@
1
+ require 'pp'
2
+
3
+ class RSpec::Varys::RSpecGenerator
4
+
5
+ def self.run
6
+
7
+ specs = YAML.load(File.read "varys.yaml")
8
+
9
+ File.open('generated_specs.rb', 'w') do |file|
10
+ process_specs(specs, file)
11
+ end
12
+
13
+ end
14
+
15
+ def self.process_specs(specs, file)
16
+ specs[:untested_stubs].each do |spec|
17
+ file.puts <<-EOF
18
+ describe #{spec[:class_name]}, "#{class_method?(spec) ? '.' : '#'}#{spec[:method]}" do
19
+
20
+ it "returns something" do
21
+ confirm(#{sut(spec)}).can receive(:#{spec[:method]})#{with_args_if_any(spec)}.and_return(#{serialize spec[:returns]})
22
+ skip "remove this line once implemented"
23
+ expect(#{sut(spec)}.#{spec[:method]}#{args_if_any(spec)}).to eq(#{serialize spec[:returns]})
24
+ end
25
+
26
+ end
27
+
28
+
29
+ EOF
30
+ end
31
+ end
32
+
33
+ def self.with_args_if_any(call)
34
+ args_if_any(call, ".with")
35
+ end
36
+
37
+ def self.args_if_any(call, prefix="")
38
+ args = call[:arguments]
39
+ (args && args.length > 0) ? "#{prefix}(#{args.map{|a| serialize a}.join ', '})" : ""
40
+ end
41
+
42
+ # Attempt to recreate the source-code to represent this argument in the setup
43
+ # for our generated spec.
44
+ def self.serialize(arg)
45
+ if %w(Array Hash Float Fixnum String NilClass TrueClass FalseClass).include? arg.class.name
46
+ arg.pretty_inspect.chop
47
+ else
48
+ guess_constructor arg
49
+ end
50
+ end
51
+
52
+ # Don't recognise the type so we don't know how to recreate it
53
+ # in source code. So we'll take a guess at what might work and
54
+ # let the user fix it up if necessary.
55
+ def self.guess_constructor(arg)
56
+ "#{arg.class.name}.new(#{serialize(arg.to_s)})"
57
+ end
58
+
59
+ def self.class_method?(call)
60
+ call[:type] == 'class'
61
+ end
62
+
63
+ def self.sut(call)
64
+ class_method?(call) ? "described_class" : "subject"
65
+ end
66
+
67
+ end
@@ -1,5 +1,5 @@
1
1
  module Rspec
2
2
  module Varys
3
- VERSION = "0.0.3"
3
+ VERSION = "0.2.0"
4
4
  end
5
5
  end
data/rspec-varys.gemspec CHANGED
@@ -18,9 +18,10 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_development_dependency "bundler", "~> 1.6"
22
- spec.add_development_dependency "rake", '~> 10.4', ">= 10.4"
21
+ spec.add_development_dependency "bundler", "~> 2.2"
22
+ spec.add_development_dependency "rake", '~> 13.0', ">= 13.0"
23
23
  spec.add_development_dependency "rspec", '~> 3.1', ">= 3.1.0"
24
- spec.add_development_dependency "cucumber", '~> 1.3', ">= 1.3.15"
25
- spec.add_development_dependency "aruba", '~> 0.6', '>= 0.6.2'
24
+ spec.add_development_dependency "cucumber", '~> 6.1', ">= 6.1.0"
25
+ spec.add_development_dependency "aruba", '~> 0.1', '>= 0.1.1'
26
+ spec.add_dependency 'pp', '~> 0.2.0'
26
27
  end
@@ -1,6 +1,6 @@
1
1
  require 'rspec'
2
2
  require 'rspec/varys'
3
- require 'pry'
3
+
4
4
 
5
5
  class Person
6
6
 
@@ -34,6 +34,7 @@ describe RSpec::Varys do
34
34
  { class_name: 'Object', message: :a_message, args: [:a_parameter], return_value: 42 }
35
35
  ])
36
36
  end
37
+
37
38
  end
38
39
 
39
40
  describe ".confirmed_messages" do
@@ -50,6 +51,17 @@ describe RSpec::Varys do
50
51
  }])
51
52
  end
52
53
 
54
+ it "parses parameters" do
55
+ described_class.reset
56
+ confirm(Person.new 'Dick', 'Jones').can receive(:full_name).with(:blah).and_return("Dick Jones")
57
+ expect(described_class.confirmed_messages).to match_array([{
58
+ class_name: 'Person',
59
+ message: :full_name,
60
+ args: [:blah],
61
+ return_value: "Dick Jones"
62
+ }])
63
+ end
64
+
53
65
  end
54
66
 
55
67
  it "records the messages sent to a spy" do
@@ -61,6 +73,7 @@ describe RSpec::Varys do
61
73
 
62
74
  expect(described_class.recorded_messages).to match_array([{
63
75
  class_name: 'Object',
76
+ type: 'instance',
64
77
  message: :a_message,
65
78
  args: [:a_parameter],
66
79
  return_value: 42
@@ -69,27 +82,13 @@ describe RSpec::Varys do
69
82
 
70
83
 
71
84
  context "given the test-suite calls a mocked method" do
72
- context "with no paramters" do
73
-
74
- let(:expected_spec) do
75
- <<GENERATED
76
- describe "#full_name" do
77
-
78
- it "returns the correct value" do
79
- pending
80
- confirm(subject).can receive(:full_name).and_return("Dick Jones")
81
- expect(subject.full_name).to eq("Dick Jones")
82
- end
83
-
84
- end
85
-
86
- GENERATED
87
- end
85
+ context "with no parameters" do
88
86
 
89
87
  let(:recognised_specs) {
90
88
  [
91
89
  {
92
90
  class_name: 'Person',
91
+ type: 'instance',
93
92
  message: :full_name,
94
93
  args: [],
95
94
  return_value: "Dick Jones"
@@ -105,38 +104,19 @@ GENERATED
105
104
  expect(dick.welcome).to eq "Welcome to OCP, I'm Dick Jones"
106
105
  end
107
106
 
108
- it "can generate required specs" do
109
- # did it correctly record the method called
107
+ it "record the methods called" do
110
108
  expect(described_class.recorded_messages).to match_array(recognised_specs)
111
-
112
- # did it generate an in-memory version of the specs?
113
- expect(described_class.generated_specs).to eq('Person' => [ expected_spec ])
114
-
115
109
  end
116
110
 
117
111
  end
118
112
 
119
113
  context "with parameters" do
120
114
 
121
- let(:expected_spec) do
122
- <<GENERATED
123
- describe "#join_names" do
124
-
125
- it "returns the correct value" do
126
- pending
127
- confirm(subject).can receive(:join_names).with("Dick", "Jones").and_return("Dick Jones")
128
- expect(subject.join_names("Dick", "Jones")).to eq("Dick Jones")
129
- end
130
-
131
- end
132
-
133
- GENERATED
134
- end
135
-
136
115
  let(:recognised_specs) {
137
116
  [
138
117
  {
139
118
  class_name: 'Person',
119
+ type: 'instance',
140
120
  message: :join_names,
141
121
  args: ["Dick", "Jones"],
142
122
  return_value: "Dick Jones"
@@ -152,13 +132,8 @@ GENERATED
152
132
  expect(dick.welcome).to eq "Welcome to OCP, I'm Dick Jones"
153
133
  end
154
134
 
155
- it "can generate required specs" do
156
- # did it correctly record the method called
135
+ it "records that the method was called" do
157
136
  expect(described_class.recorded_messages).to match_array(recognised_specs)
158
-
159
- # did it generate an in-memory version of the specs?
160
- expect(described_class.generated_specs).to eq('Person' => [ expected_spec ])
161
-
162
137
  end
163
138
 
164
139
  end
@@ -0,0 +1,4 @@
1
+
2
+ Dir.glob(::File.expand_path('../support/*.rb', __FILE__)).each { |f| require_relative f }
3
+ Dir.glob(::File.expand_path('../../lib/*.rb', __FILE__)).each { |f| require_relative f }
4
+
@@ -0,0 +1,78 @@
1
+ require 'spec_helper'
2
+ require_relative '../../lib/rspec/varys/rspec_generator'
3
+
4
+ describe RSpec::Varys::RSpecGenerator do
5
+
6
+ describe ".process_specs" do
7
+
8
+ context "given a file YAML file with one missing spec with arguments" do
9
+ let(:specs) {
10
+ YAML.load <<-SPECS
11
+ ---
12
+ :untested_stubs:
13
+ - :class_name: Person
14
+ :method: full_name
15
+ :arguments:
16
+ - Dick
17
+ - Jones
18
+ :returns: Dick Jones
19
+ SPECS
20
+ }
21
+
22
+ it "generates the spec" do
23
+ output = ""
24
+ StringIO.open(output, 'w') do |file|
25
+ described_class.process_specs(specs, file)
26
+ end
27
+ expect(output).to eq <<-EOF
28
+ describe Person, "#full_name" do
29
+
30
+ it "returns something" do
31
+ confirm(subject).can receive(:full_name).with("Dick", "Jones").and_return("Dick Jones")
32
+ skip "remove this line once implemented"
33
+ expect(subject.full_name("Dick", "Jones")).to eq("Dick Jones")
34
+ end
35
+
36
+ end
37
+
38
+
39
+ EOF
40
+ end
41
+ end
42
+
43
+
44
+ context "given a file YAML file with one missing spec" do
45
+ let(:specs) {
46
+ YAML.load <<-SPECS
47
+ ---
48
+ :untested_stubs:
49
+ - :class_name: Person
50
+ :method: full_name
51
+ :returns: Dick Jones
52
+ SPECS
53
+ }
54
+
55
+ it "generates the spec" do
56
+ output = ""
57
+ StringIO.open(output, 'w') do |file|
58
+ described_class.process_specs(specs, file)
59
+ end
60
+ expect(output).to eq <<-EOF
61
+ describe Person, "#full_name" do
62
+
63
+ it "returns something" do
64
+ confirm(subject).can receive(:full_name).and_return("Dick Jones")
65
+ skip "remove this line once implemented"
66
+ expect(subject.full_name).to eq("Dick Jones")
67
+ end
68
+
69
+ end
70
+
71
+
72
+ EOF
73
+ end
74
+ end
75
+
76
+ end
77
+
78
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-varys
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ritchie Young
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-01-27 00:00:00.000000000 Z
11
+ date: 2021-06-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,34 +16,34 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.6'
19
+ version: '2.2'
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: '1.6'
26
+ version: '2.2'
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: '10.4'
33
+ version: '13.0'
34
34
  - - ">="
35
35
  - !ruby/object:Gem::Version
36
- version: '10.4'
36
+ version: '13.0'
37
37
  type: :development
38
38
  prerelease: false
39
39
  version_requirements: !ruby/object:Gem::Requirement
40
40
  requirements:
41
41
  - - "~>"
42
42
  - !ruby/object:Gem::Version
43
- version: '10.4'
43
+ version: '13.0'
44
44
  - - ">="
45
45
  - !ruby/object:Gem::Version
46
- version: '10.4'
46
+ version: '13.0'
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: rspec
49
49
  requirement: !ruby/object:Gem::Requirement
@@ -70,62 +70,83 @@ dependencies:
70
70
  requirements:
71
71
  - - "~>"
72
72
  - !ruby/object:Gem::Version
73
- version: '1.3'
73
+ version: '6.1'
74
74
  - - ">="
75
75
  - !ruby/object:Gem::Version
76
- version: 1.3.15
76
+ version: 6.1.0
77
77
  type: :development
78
78
  prerelease: false
79
79
  version_requirements: !ruby/object:Gem::Requirement
80
80
  requirements:
81
81
  - - "~>"
82
82
  - !ruby/object:Gem::Version
83
- version: '1.3'
83
+ version: '6.1'
84
84
  - - ">="
85
85
  - !ruby/object:Gem::Version
86
- version: 1.3.15
86
+ version: 6.1.0
87
87
  - !ruby/object:Gem::Dependency
88
88
  name: aruba
89
89
  requirement: !ruby/object:Gem::Requirement
90
90
  requirements:
91
91
  - - "~>"
92
92
  - !ruby/object:Gem::Version
93
- version: '0.6'
93
+ version: '0.1'
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
- version: 0.6.2
96
+ version: 0.1.1
97
97
  type: :development
98
98
  prerelease: false
99
99
  version_requirements: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: '0.6'
103
+ version: '0.1'
104
104
  - - ">="
105
105
  - !ruby/object:Gem::Version
106
- version: 0.6.2
106
+ version: 0.1.1
107
+ - !ruby/object:Gem::Dependency
108
+ name: pp
109
+ requirement: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - "~>"
112
+ - !ruby/object:Gem::Version
113
+ version: 0.2.0
114
+ type: :runtime
115
+ prerelease: false
116
+ version_requirements: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - "~>"
119
+ - !ruby/object:Gem::Version
120
+ version: 0.2.0
107
121
  description: Automatically track which assumptions you've made in the form or mocks
108
122
  and stubs actually work.
109
123
  email:
110
124
  - ritchiey@gmail.com
111
- executables: []
125
+ executables:
126
+ - genspecs
112
127
  extensions: []
113
128
  extra_rdoc_files: []
114
129
  files:
115
130
  - ".gitignore"
116
131
  - ".ruby-version"
132
+ - ".tool-versions"
133
+ - ".travis.yml"
117
134
  - Gemfile
118
- - Gemfile.lock
119
135
  - LICENSE.txt
120
136
  - README.md
121
137
  - Rakefile
138
+ - bin/genspecs
139
+ - config/cucumber.yml
122
140
  - features/generating_specs.feature
123
141
  - features/readme.md
124
- - features/support/env.rb
142
+ - features/support/external.rb
125
143
  - lib/rspec/varys.rb
144
+ - lib/rspec/varys/rspec_generator.rb
126
145
  - lib/rspec/varys/version.rb
127
146
  - rspec-varys.gemspec
128
147
  - spec/rspec/varys_spec.rb
148
+ - spec/spec_helper.rb
149
+ - spec/varys/rspec_generator_spec.rb
129
150
  homepage: https://github.com/ritchiey/rspec-varys
130
151
  licenses:
131
152
  - MIT
@@ -145,14 +166,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
145
166
  - !ruby/object:Gem::Version
146
167
  version: '0'
147
168
  requirements: []
148
- rubyforge_project:
149
- rubygems_version: 2.2.2
169
+ rubygems_version: 3.2.15
150
170
  signing_key:
151
171
  specification_version: 4
152
172
  summary: Generate RSpec specs from intelligence gathered from doubles and spies.
153
173
  test_files:
154
174
  - features/generating_specs.feature
155
175
  - features/readme.md
156
- - features/support/env.rb
176
+ - features/support/external.rb
157
177
  - spec/rspec/varys_spec.rb
158
- has_rdoc:
178
+ - spec/spec_helper.rb
179
+ - spec/varys/rspec_generator_spec.rb