rspec-stepwise 0.1.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 +7 -0
- data/.gitignore +13 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +127 -0
- data/Rakefile +6 -0
- data/lib/rspec/stepwise/version.rb +5 -0
- data/lib/rspec/stepwise.rb +135 -0
- data/rspec-stepwise.gemspec +22 -0
- metadata +95 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 5a90ad69a86866bac6f5cd8d5c163f9cad892df41a20639bf4424c2fc05e621f
|
4
|
+
data.tar.gz: b7f440c808536c92caaaa466412b394193c16d393c9a74b3cc473da342f080b0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: dc3bce1e9aa483eac641b691d509fa38792140e29ce32508ae091a315f087e64ad443a17b425c9355232adc0341dd9ad1c80bf94b484888c5addade74bf18b14
|
7
|
+
data.tar.gz: 995132c1190c4fcbed1cc74a2b5dba44b8cb22c6ad0c41b87420b92885598c16b8b800ef3d40a0aeeafaec95f48ad806975bd05b927fa3380584d672a8a0e934
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2019 Nikita Afanasenko
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
# RSpec Stepwise Executor
|
2
|
+
|
3
|
+
Provides a simple DSL on top of RSpec for defining a series of spec execution steps. Particularly useful for describing of human-readable integration testing scenarios.
|
4
|
+
|
5
|
+
RSpec Stepwise provides the following guarantees within the same steps group:
|
6
|
+
|
7
|
+
- Strict step execution order, even if RSpec is configured to run examples randomly.
|
8
|
+
- All steps share the same execution context, thus share instances defined in `let` etc.
|
9
|
+
- All steps after the failed one will be skipped.
|
10
|
+
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
Configure RSpec to use stepwise DSL:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
require 'rspec/stepwise'
|
17
|
+
|
18
|
+
RSpec.configure do |config|
|
19
|
+
config.extend RSpec::Stepwise
|
20
|
+
# ...
|
21
|
+
end
|
22
|
+
```
|
23
|
+
|
24
|
+
This adds `stepwise` method for creating ordered example groups and `step` method for describing particular step in a chain. See full usage example:
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
RSpec.describe 'User Scenarios' do
|
28
|
+
# Defines example group for account confirmation scenario.
|
29
|
+
# All steps within this group will run in an order.
|
30
|
+
stepwise 'Account Confirmation' do
|
31
|
+
# All RSpec APIs works as usual inside both group and steps.
|
32
|
+
let(:api) { ClientApi.new }
|
33
|
+
let(:mailbox) { Mailbox.new }
|
34
|
+
let(:user) { User.new }
|
35
|
+
|
36
|
+
step 'register' do
|
37
|
+
api.register(user)
|
38
|
+
end
|
39
|
+
|
40
|
+
step 'unable to sign in' do
|
41
|
+
response = api.sign_in(user)
|
42
|
+
expect(response).to be_forbidden
|
43
|
+
end
|
44
|
+
|
45
|
+
step 'confirm' do
|
46
|
+
mailbox.confirm(user)
|
47
|
+
fail 'Email not found in mailbox'
|
48
|
+
end
|
49
|
+
|
50
|
+
# This step will not be executed because previous one failed.
|
51
|
+
step 'successfully sign in' do
|
52
|
+
response = api.sign_in(user)
|
53
|
+
expect(response).to be_successfull
|
54
|
+
end
|
55
|
+
|
56
|
+
# Called after all steps, even if one failed.
|
57
|
+
after do
|
58
|
+
mailbox.clear
|
59
|
+
end
|
60
|
+
|
61
|
+
# Called only when a step failed.
|
62
|
+
on_fail do
|
63
|
+
puts api.logs
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
```
|
68
|
+
|
69
|
+
## Known Limitations
|
70
|
+
|
71
|
+
RSpec doubles should not be used in a shared stepwise context, otherwise it leads to:
|
72
|
+
|
73
|
+
_#<Double ...> was originally created in one example but has leaked into another example and can no longer be used. rspec-mocks' doubles are designed to only last for one example, and you need to create a new one in each example you wish to use it for._
|
74
|
+
|
75
|
+
So this will not work:
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
stepwise do
|
79
|
+
let(:api) { double(:api, call: nil) }
|
80
|
+
|
81
|
+
step 'one' do
|
82
|
+
api.call
|
83
|
+
end
|
84
|
+
|
85
|
+
step 'another' do
|
86
|
+
api.call
|
87
|
+
end
|
88
|
+
end
|
89
|
+
```
|
90
|
+
|
91
|
+
Instead you have to do something like:
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
stepwise do
|
95
|
+
step 'one' do
|
96
|
+
api = double(:api, call: nil)
|
97
|
+
api.call
|
98
|
+
end
|
99
|
+
|
100
|
+
step 'another' do
|
101
|
+
api = double(:api, call: nil)
|
102
|
+
api.call
|
103
|
+
end
|
104
|
+
end
|
105
|
+
```
|
106
|
+
|
107
|
+
## Installation
|
108
|
+
|
109
|
+
Using Bundler:
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
gem 'rspec-stepwise', '~> 0.1.0'
|
113
|
+
```
|
114
|
+
|
115
|
+
Or using rubygems:
|
116
|
+
|
117
|
+
```bash
|
118
|
+
gem install rspec-stepwise
|
119
|
+
```
|
120
|
+
|
121
|
+
## Contributing
|
122
|
+
|
123
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/nikitug/rspec-stepwise.
|
124
|
+
|
125
|
+
## License
|
126
|
+
|
127
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'rspec/stepwise/version'
|
2
|
+
|
3
|
+
module RSpec
|
4
|
+
# Provides DSL for defining a series of steps.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# RSpec.describe 'user registration and sign in' do
|
8
|
+
# stepwise do
|
9
|
+
# step 'register' do
|
10
|
+
# api.register(user)
|
11
|
+
# mailbox.confirm(user)
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# step 'sign in' do
|
15
|
+
# token = api.sign_in(user)
|
16
|
+
# expect(token).not_to be expired
|
17
|
+
# end
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
module Stepwise
|
22
|
+
# Defines new series of steps. Supports the same arguments as `RSpec.describe`.
|
23
|
+
# @see RSpec.describe
|
24
|
+
def stepwise(name = nil, *args, &block)
|
25
|
+
if args.last.is_a?(Hash)
|
26
|
+
args.last[:order] = :defined
|
27
|
+
else
|
28
|
+
args << { order: :defined }
|
29
|
+
end
|
30
|
+
describe(name, *args) do
|
31
|
+
Builder.new(self).instance_eval(&block)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Provides DSL for steps definition and builds execution context.
|
36
|
+
class Builder
|
37
|
+
# @param klass [RSpec::Core::ExampleGroup]
|
38
|
+
def initialize(klass)
|
39
|
+
@klass = klass
|
40
|
+
@steps_context = Context.new(klass)
|
41
|
+
@fail_callbacks = []
|
42
|
+
end
|
43
|
+
|
44
|
+
# Defines new step in a series. Supports the same arguments as `RSpec::Core::ExampleGroup.it`.
|
45
|
+
# @see RSpec::Core::ExampleGroup.it
|
46
|
+
def step(*args, &block)
|
47
|
+
# `it` defines method within `klass`, so Builder's
|
48
|
+
# instance variable will not be visible there,
|
49
|
+
# so we use local var.
|
50
|
+
steps_context = @steps_context
|
51
|
+
fail_callbacks = @fail_callbacks
|
52
|
+
@klass.it(*args) do
|
53
|
+
if steps_context.previous_failed?
|
54
|
+
pending 'Previous step failed'
|
55
|
+
fail
|
56
|
+
else
|
57
|
+
begin
|
58
|
+
steps_context.run_step(&block)
|
59
|
+
rescue
|
60
|
+
fail_callbacks.each do |callback|
|
61
|
+
steps_context.run(&callback)
|
62
|
+
end
|
63
|
+
raise
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Runs if any step failed. Can be executed multiple times.
|
70
|
+
# Execution will happen in the same order as definition order.
|
71
|
+
#
|
72
|
+
# @example
|
73
|
+
# # Outputs API logs in case of failure.
|
74
|
+
# on_fail do
|
75
|
+
# puts api.execution_logs
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
def on_fail(&block)
|
79
|
+
@fail_callbacks << block
|
80
|
+
end
|
81
|
+
|
82
|
+
# Runs after all steps finished.
|
83
|
+
#
|
84
|
+
# @example
|
85
|
+
# # Clears virtual mailbox after all steps.
|
86
|
+
# after do
|
87
|
+
# mailbox.clear
|
88
|
+
# end
|
89
|
+
#
|
90
|
+
def after(&block)
|
91
|
+
steps_context = @steps_context
|
92
|
+
@klass.after(:all) do
|
93
|
+
steps_context.run(&block)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
def method_missing(name, *args, &block)
|
100
|
+
if @klass.respond_to?(name)
|
101
|
+
@klass.public_send(name, *args, &block)
|
102
|
+
else
|
103
|
+
super
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def respond_to_missing?(name, include_private = false)
|
108
|
+
@klass.respond_to?(name, include_private) || super
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# @api private
|
113
|
+
class Context
|
114
|
+
def initialize(klass)
|
115
|
+
@context = klass.new
|
116
|
+
@previous_failed = false
|
117
|
+
end
|
118
|
+
|
119
|
+
def run_step(&block)
|
120
|
+
run(&block)
|
121
|
+
rescue
|
122
|
+
@previous_failed = true
|
123
|
+
raise
|
124
|
+
end
|
125
|
+
|
126
|
+
def run(&block)
|
127
|
+
@context.instance_eval(&block)
|
128
|
+
end
|
129
|
+
|
130
|
+
def previous_failed?
|
131
|
+
@previous_failed
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require_relative "lib/rspec/stepwise/version"
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = "rspec-stepwise"
|
5
|
+
spec.version = RSpec::Stepwise::VERSION
|
6
|
+
spec.authors = ["Nikita Afanasenko"]
|
7
|
+
spec.email = ["nikita@afanasenko.name"]
|
8
|
+
|
9
|
+
spec.summary = "Stepwise execution for RSpec."
|
10
|
+
spec.description = "Stepwise execution for RSpec."
|
11
|
+
spec.homepage = "https://github.com/nikitug/rspec-stepwise"
|
12
|
+
spec.license = "MIT"
|
13
|
+
|
14
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
15
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
16
|
+
end
|
17
|
+
spec.require_paths = ["lib"]
|
18
|
+
|
19
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
20
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
21
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rspec-stepwise
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nikita Afanasenko
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-06-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
description: Stepwise execution for RSpec.
|
56
|
+
email:
|
57
|
+
- nikita@afanasenko.name
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- ".gitignore"
|
63
|
+
- ".rspec"
|
64
|
+
- ".travis.yml"
|
65
|
+
- Gemfile
|
66
|
+
- LICENSE.txt
|
67
|
+
- README.md
|
68
|
+
- Rakefile
|
69
|
+
- lib/rspec/stepwise.rb
|
70
|
+
- lib/rspec/stepwise/version.rb
|
71
|
+
- rspec-stepwise.gemspec
|
72
|
+
homepage: https://github.com/nikitug/rspec-stepwise
|
73
|
+
licenses:
|
74
|
+
- MIT
|
75
|
+
metadata: {}
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options: []
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
requirements: []
|
91
|
+
rubygems_version: 3.0.1
|
92
|
+
signing_key:
|
93
|
+
specification_version: 4
|
94
|
+
summary: Stepwise execution for RSpec.
|
95
|
+
test_files: []
|