rspec-varys 0.0.3 → 0.2.0

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
- 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