specify 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/README.md +123 -3
- data/Rakefile +0 -1
- data/lib/specify/rspec/documentation_formatter.rb +57 -0
- data/lib/specify/rspec/example_group.rb +32 -0
- data/lib/specify/rspec/notification.rb +6 -0
- data/lib/specify/rspec/reporter.rb +45 -0
- data/lib/specify/rspec/shared_steps.rb +18 -0
- data/lib/specify/rspec/world.rb +9 -0
- data/lib/specify/version.rb +1 -1
- data/lib/specify.rb +37 -0
- data/specify.gemspec +2 -3
- metadata +10 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0e886dff9cb121d9b139f79b7bbfbe0fbfb015d6
|
4
|
+
data.tar.gz: ba3900c1895de6e1e3c42d0709ce770fa708bdce
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 11d8a7eacd4b0d70047d270d1153cab8700bca9a40419e7930ff9a49bf684af953d0bd7cf3bb3095cf1150189768d320c6a74043a09a4b533ef24f783f0725cd
|
7
|
+
data.tar.gz: dd1977da7b7b94864500fa533eb859c6cd731d5eac70a9609513273fd85f8f0c571bd367c19299e94deb5c0accb8aae0fa6eca3c7a20acbe459049d955e190eb
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# Specify
|
2
2
|
|
3
|
-
Specify
|
3
|
+
Specify is a BDD-style micro-framework.
|
4
4
|
|
5
|
-
|
5
|
+
Specify is a very thin wrapper around RSpec that provides a Gherkin-style syntax for use with test examples.
|
6
6
|
|
7
7
|
## Installation
|
8
8
|
|
@@ -22,7 +22,119 @@ Or install it yourself as:
|
|
22
22
|
|
23
23
|
## Usage
|
24
24
|
|
25
|
-
|
25
|
+
To use Specify you simply have to require it within your `spec_helper` file.
|
26
|
+
|
27
|
+
While Specify does provide a Gherkin-like syntax, there is no parsing of a Gherkin feature and step definition matching. Just as in RSpec, everything is in one place: the Ruby file. The benefit here is that you don't have to change the text in two places (feature file and step definition file) every time you change something. Further, there are no more matchers to sync up with natural language. Specify uses plain Ruby helper methods coupled with various patterns.
|
28
|
+
|
29
|
+
### Integration Tests
|
30
|
+
|
31
|
+
RSpec's mode of action is that all examples should be completely independent. This is in line with its focus as primarily a unit-based testing framework. For integration purposes, this means if you want to use RSpec you have to write a sequence of examples, each of which repeats the behavior of all previous examples. Another alternative is that you could write one single large example that performs the entire set of actions. The problem in that case is that there is no independent reporting of each step.
|
32
|
+
|
33
|
+
This is why tools like Cucumber end up being used.
|
34
|
+
|
35
|
+
However the only benefit there is the ability to execute some examples in sequence and skip subsequent steps after a failure.
|
36
|
+
|
37
|
+
So that's the goal of Specify: provide just the good parts of Cucumber and skipping all the questionable parts. At minimum this means the ability to chain examples into a series of steps that run in sequence and which stop when a step fails. The idea is to assemble a series of tests that should all pass, but where completely isolating them is not sensible. Hooking this into RSpec, this would make RSpec less unit and more integration.
|
38
|
+
|
39
|
+
### Example
|
40
|
+
|
41
|
+
Consider this example of a standard RSpec example group:
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
describe 'Basic Spec Structure' do
|
45
|
+
context 'when simple logic tests are applied' do
|
46
|
+
it 'will agree that true almost certainly not false' do
|
47
|
+
expect(true).to_not be false
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'will agree that true is pretty definitely true' do
|
51
|
+
expect(true).to be true
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe 'Simple Scenarios' do
|
57
|
+
context 'with an instance variable' do
|
58
|
+
it 'will establish a variable' do
|
59
|
+
@active = 'testing'
|
60
|
+
expect(@active).to eq 'testing'
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'will reference the instance variable' do
|
64
|
+
puts "@active = #{@active}"
|
65
|
+
expect(@active).to eq 'testing'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
```
|
70
|
+
|
71
|
+
Now consider how this can work with Specify. You will have a "steps" block. Within that, all examples run in order of specification. State is preserved between examples within the "steps" block. If any example inside the "steps" block fails, all remaining steps will be marked as pending and thus skipped.
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
context 'when simple logic tests are applied' do
|
75
|
+
steps do
|
76
|
+
Given 'true is almost certainly not false' do
|
77
|
+
expect(true).to_not be false
|
78
|
+
end
|
79
|
+
|
80
|
+
Given 'true is pretty definitely true' do
|
81
|
+
expect(true).to be true
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'Simple Scenarios' do
|
87
|
+
steps 'with an instance variable' do
|
88
|
+
When 'it establishes an instance variable' do
|
89
|
+
@active = 'testing'
|
90
|
+
expect(@active).to eq 'testing'
|
91
|
+
end
|
92
|
+
|
93
|
+
Then 'that instance variable can be referenced' do
|
94
|
+
puts "@active = #{@active}"
|
95
|
+
expect(@active).to eq 'testing'
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
```
|
100
|
+
|
101
|
+
### Shared Steps
|
102
|
+
|
103
|
+
You can use `shared_steps` to define a block that will be evaluated in the context of an example. The example must use the `include_steps` directive. Here is an example of some shared steps:
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
shared_steps 'login' do |email, password|
|
107
|
+
When 'I go to login page' do
|
108
|
+
puts 'Go to the login page'
|
109
|
+
end
|
110
|
+
|
111
|
+
When 'I put credentials' do
|
112
|
+
puts "Email: #{email}, Password: #{password}"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
shared_steps 'invalid login' do
|
117
|
+
Then 'I should see login error' do
|
118
|
+
puts 'Expect a login error'
|
119
|
+
end
|
120
|
+
end
|
121
|
+
```
|
122
|
+
|
123
|
+
Here is how those steps can be used:
|
124
|
+
|
125
|
+
```ruby
|
126
|
+
context 'action' do
|
127
|
+
steps 'User provides wrong email' do
|
128
|
+
include_steps 'login', 'jeff', 'invalid'
|
129
|
+
include_steps 'invalid login'
|
130
|
+
end
|
131
|
+
|
132
|
+
steps 'User provides wrong password' do
|
133
|
+
include_steps 'login', 'jeff@example.com', 'testing'
|
134
|
+
include_steps 'invalid login'
|
135
|
+
end
|
136
|
+
end
|
137
|
+
```
|
26
138
|
|
27
139
|
## Contributing
|
28
140
|
|
@@ -33,3 +145,11 @@ To work on Specify:
|
|
33
145
|
3. Commit your changes. (`git commit -am 'new feature'`)
|
34
146
|
4. Push the branch. (`git push origin my-new-feature`)
|
35
147
|
5. Create a new [pull request](https://help.github.com/articles/using-pull-requests).
|
148
|
+
|
149
|
+
## Authors
|
150
|
+
|
151
|
+
* [Jeff Nyman](http://testerstories.com)
|
152
|
+
|
153
|
+
## License
|
154
|
+
|
155
|
+
Specify is distributed under the [MIT](http://www.opensource.org/licenses/MIT) license.
|
data/Rakefile
CHANGED
@@ -0,0 +1,57 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Specify
|
3
|
+
module DocumentationFormatter
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval do
|
6
|
+
include InstanceMethods
|
7
|
+
|
8
|
+
alias :example_started_without_steps :example_started
|
9
|
+
alias :example_started :example_started_with_steps
|
10
|
+
|
11
|
+
alias :example_passed_without_steps :example_passed
|
12
|
+
alias :example_passed :example_passed_with_steps
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module InstanceMethods
|
18
|
+
def example_started(notification)
|
19
|
+
end
|
20
|
+
|
21
|
+
def example_step_passed(notification)
|
22
|
+
full_message = "#{current_indentation} #{notification.type.to_s.capitalize} #{notification.message}"
|
23
|
+
output.puts Core::Formatters::ConsoleCodes.wrap(full_message, :success)
|
24
|
+
end
|
25
|
+
|
26
|
+
def example_step_failed(notification)
|
27
|
+
full_message = "#{current_indentation} #{notification.type.to_s.capitalize} #{notification.message} (FAILED)"
|
28
|
+
output.puts Core::Formatters::ConsoleCodes.wrap(full_message, :failure)
|
29
|
+
end
|
30
|
+
|
31
|
+
def example_step_pending(notification)
|
32
|
+
full_message = "#{current_indentation} #{notification.type.to_s.capitalize} #{notification.message}"
|
33
|
+
|
34
|
+
if notification.options[:pending] && notification.options[:pending] != true
|
35
|
+
full_message << " (PENDING: #{notification.options[:pending]})"
|
36
|
+
else
|
37
|
+
full_message << ' (PENDING)'
|
38
|
+
end
|
39
|
+
|
40
|
+
output.puts Core::Formatters::ConsoleCodes.wrap(full_message, :pending)
|
41
|
+
end
|
42
|
+
|
43
|
+
def example_started_with_steps(notification)
|
44
|
+
example_started_without_steps(notification)
|
45
|
+
|
46
|
+
if notification.example.metadata[:with_steps]
|
47
|
+
full_message = "#{current_indentation}#{notification.example.description}"
|
48
|
+
output.puts Core::Formatters::ConsoleCodes.wrap(full_message, :default)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def example_passed_with_steps(notification)
|
53
|
+
example_passed_without_steps(notification) unless notification.example.metadata[:with_steps]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Specify
|
3
|
+
module ExampleGroup
|
4
|
+
def include_steps(*args)
|
5
|
+
name = args.shift
|
6
|
+
shared_block = RSpec.world.shared_example_steps[name]
|
7
|
+
shared_block or raise ArgumentError, "Could not find shared steps #{name.inspect}"
|
8
|
+
instance_exec(*args, &shared_block)
|
9
|
+
end
|
10
|
+
|
11
|
+
def Given(message, options={}, &block)
|
12
|
+
RSpec.world.reporter.process_example_step(self, :given, message, options, &block)
|
13
|
+
end
|
14
|
+
|
15
|
+
def When(message, options={}, &block)
|
16
|
+
RSpec.world.reporter.process_example_step(self, :when, message, options, &block)
|
17
|
+
end
|
18
|
+
|
19
|
+
def Then(message, options={}, &block)
|
20
|
+
RSpec.world.reporter.process_example_step(self, :then, message, options, &block)
|
21
|
+
end
|
22
|
+
|
23
|
+
def And(message, options = {}, &block)
|
24
|
+
RSpec.world.reporter.process_example_step(self, :and, message, options, &block)
|
25
|
+
end
|
26
|
+
|
27
|
+
def But(message, options = {}, &block)
|
28
|
+
RSpec.world.reporter.process_example_step(self, :but, message, options, &block)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Specify
|
3
|
+
module Reporter
|
4
|
+
def process_example_step(example, type, message, options)
|
5
|
+
example_step_started(self, type, message, options)
|
6
|
+
|
7
|
+
if block_given? && !options[:pending]
|
8
|
+
begin
|
9
|
+
yield
|
10
|
+
rescue Exception => e
|
11
|
+
example_step_failed(self, type, message, options)
|
12
|
+
raise e
|
13
|
+
end
|
14
|
+
example_step_passed(self, type, message, options)
|
15
|
+
else
|
16
|
+
example_step_pending(self, type, message, options)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def example_step_started(example, type, message, options)
|
21
|
+
notify :example_step_started, Notification.new(example, type, message, options)
|
22
|
+
end
|
23
|
+
|
24
|
+
def example_step_passed(example, type, message, options)
|
25
|
+
notify :example_step_passed, Notification.new(example, type, message, options)
|
26
|
+
end
|
27
|
+
|
28
|
+
def example_step_failed(example, type, message, options)
|
29
|
+
notify :example_step_failed, Notification.new(example, type, message, options)
|
30
|
+
end
|
31
|
+
|
32
|
+
def example_step_pending(example, type, message, options)
|
33
|
+
notify :example_step_pending, Notification.new(example, type, message, options)
|
34
|
+
end
|
35
|
+
|
36
|
+
def registered_formatters
|
37
|
+
@listeners.values.map(&:to_a).flatten.uniq
|
38
|
+
end
|
39
|
+
|
40
|
+
def find_registered_formatter(cls)
|
41
|
+
registered_formatters.detect { |formatter| formatter.class == cls }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Specify
|
3
|
+
module SharedSteps
|
4
|
+
def shared_steps(name, &block)
|
5
|
+
ensure_shared_example_steps_name_not_taken(name)
|
6
|
+
RSpec.world.shared_example_steps[name] = block
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def ensure_shared_example_steps_name_not_taken(name)
|
12
|
+
if RSpec.world.shared_example_steps.has_key?(name)
|
13
|
+
raise ArgumentError.new("Shared step '#{name}' already exists")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/specify/version.rb
CHANGED
data/lib/specify.rb
CHANGED
@@ -2,3 +2,40 @@ require 'specify/version'
|
|
2
2
|
|
3
3
|
module Specify
|
4
4
|
end
|
5
|
+
|
6
|
+
require 'rspec/core'
|
7
|
+
require 'rspec/core/example_group'
|
8
|
+
require 'rspec/core/reporter'
|
9
|
+
require 'rspec/core/world'
|
10
|
+
require 'rspec/core/formatters'
|
11
|
+
require 'rspec/core/formatters/console_codes'
|
12
|
+
require 'rspec/core/formatters/documentation_formatter'
|
13
|
+
|
14
|
+
require 'specify/rspec/example_group'
|
15
|
+
require 'specify/rspec/notification'
|
16
|
+
require 'specify/rspec/reporter'
|
17
|
+
require 'specify/rspec/world'
|
18
|
+
require 'specify/rspec/documentation_formatter'
|
19
|
+
|
20
|
+
RSpec::Core::ExampleGroup.send :include, RSpec::Specify::ExampleGroup
|
21
|
+
RSpec::Core::Reporter.send :include, RSpec::Specify::Reporter
|
22
|
+
RSpec::Core::World.send :include, RSpec::Specify::World
|
23
|
+
|
24
|
+
RSpec::Core::Formatters::DocumentationFormatter.send :include, RSpec::Specify::DocumentationFormatter
|
25
|
+
|
26
|
+
if formatter = RSpec.world.reporter.find_registered_formatter(RSpec::Core::Formatters::DocumentationFormatter)
|
27
|
+
RSpec.world.reporter.register_listener(
|
28
|
+
formatter,
|
29
|
+
:example_started,
|
30
|
+
:example_step_passed,
|
31
|
+
:example_step_pending,
|
32
|
+
:example_step_failed
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
RSpec::Core::ExampleGroup.define_example_method :steps, with_steps: true
|
38
|
+
RSpec::Core::ExampleGroup.define_example_method :Scenario, with_steps: true
|
39
|
+
|
40
|
+
require 'specify/rspec/shared_steps'
|
41
|
+
include RSpec::Specify::SharedSteps
|
data/specify.gemspec
CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.files = `git ls-files -z`.split("\x0")
|
22
22
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
23
23
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
24
|
-
spec.require_paths =
|
24
|
+
spec.require_paths = ['lib']
|
25
25
|
|
26
26
|
spec.required_ruby_version = '>= 2.0.0'
|
27
27
|
spec.required_rubygems_version = '>= 1.8.29'
|
@@ -29,8 +29,7 @@ Gem::Specification.new do |spec|
|
|
29
29
|
spec.add_development_dependency 'bundler', '~> 1.7'
|
30
30
|
spec.add_development_dependency 'rake', '~> 10.0'
|
31
31
|
|
32
|
-
spec.add_runtime_dependency '
|
33
|
-
spec.add_runtime_dependency 'rspec', '~> 3.0'
|
32
|
+
spec.add_runtime_dependency 'rspec-core', '~> 3.0'
|
34
33
|
|
35
34
|
spec.post_install_message = %{
|
36
35
|
(::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: specify
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeff Nyman
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-10-
|
11
|
+
date: 2014-10-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -39,21 +39,7 @@ dependencies:
|
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '10.0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - "~>"
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '2.12'
|
48
|
-
type: :runtime
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - "~>"
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '2.12'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: rspec
|
42
|
+
name: rspec-core
|
57
43
|
requirement: !ruby/object:Gem::Requirement
|
58
44
|
requirements:
|
59
45
|
- - "~>"
|
@@ -82,6 +68,12 @@ files:
|
|
82
68
|
- README.md
|
83
69
|
- Rakefile
|
84
70
|
- lib/specify.rb
|
71
|
+
- lib/specify/rspec/documentation_formatter.rb
|
72
|
+
- lib/specify/rspec/example_group.rb
|
73
|
+
- lib/specify/rspec/notification.rb
|
74
|
+
- lib/specify/rspec/reporter.rb
|
75
|
+
- lib/specify/rspec/shared_steps.rb
|
76
|
+
- lib/specify/rspec/world.rb
|
85
77
|
- lib/specify/version.rb
|
86
78
|
- specify.gemspec
|
87
79
|
homepage: https://github.com/jnyman/specify
|
@@ -89,7 +81,7 @@ licenses:
|
|
89
81
|
- MIT
|
90
82
|
metadata: {}
|
91
83
|
post_install_message: "\n(::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::)\n\n
|
92
|
-
\ Specify 0.0
|
84
|
+
\ Specify 0.1.0 has been installed.\n\n(::) (::) (::) (::) (::) (::) (::) (::) (::)
|
93
85
|
(::) (::) (::)\n "
|
94
86
|
rdoc_options: []
|
95
87
|
require_paths:
|