specdown 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.markdown +11 -0
- data/README.markdown +171 -1
- data/features/command.feature +38 -35
- data/features/config.feature +96 -0
- data/features/event_server.feature +39 -0
- data/features/fixtures/parser_example.markdown +2 -2
- data/features/parser.feature +30 -30
- data/features/runner.feature +12 -20
- data/features/specdown_examples/no_ruby/specdown/parser_example.markdown +3 -2
- data/features/specdown_examples/with_ruby/specdown/env.rb +1 -2
- data/features/specdown_examples/with_ruby/specdown/parser_example.markdown +3 -2
- data/features/step_definitions/config.rb +34 -0
- data/features/step_definitions/event_server.rb +19 -0
- data/features/step_definitions/parser.rb +1 -1
- data/features/support/env.rb +0 -1
- data/features/support/hooks.rb +3 -0
- data/lib/specdown/command.rb +1 -8
- data/lib/specdown/config.rb +11 -0
- data/lib/specdown/event_handlers/run_complete.rb +3 -0
- data/lib/specdown/event_handlers/run_started.rb +0 -0
- data/lib/specdown/event_handlers/test_failed.rb +3 -0
- data/lib/specdown/event_handlers/test_passed.rb +3 -0
- data/lib/specdown/event_server.rb +19 -0
- data/lib/specdown/parser.rb +1 -0
- data/lib/specdown/runner/report.rb +10 -1
- data/lib/specdown/runner/stats.rb +3 -1
- data/lib/specdown/runner.rb +16 -10
- data/lib/specdown/sandbox_factory.rb +56 -0
- data/lib/specdown/specdown.rb +7 -0
- data/lib/specdown.rb +8 -1
- metadata +24 -7
- data/SPEC.markdown +0 -122
- data/lib/specdown/sandbox.rb +0 -4
data/CHANGELOG.markdown
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
## CHANGELOG
|
2
|
+
|
3
|
+
## 0.1.0 (2012/01/01)
|
4
|
+
|
5
|
+
Better Sandboxing of tests, with support for configuring the expectation/assertion framework (RSpec Expectations and Test::Unit asserstions supported)
|
6
|
+
|
7
|
+
See the "Setting up your test environment" section in the README for more information (https://github.com/moonmaster9000/specdown/blob/ccd9aea13173d582241ca39e6f4f10d50fab4490/README.markdown).
|
8
|
+
|
9
|
+
## 0.0.1 (2011/12/30)
|
10
|
+
|
11
|
+
First release! Barely passable! :-)
|
data/README.markdown
CHANGED
@@ -1 +1,171 @@
|
|
1
|
-
|
1
|
+
# specdown
|
2
|
+
|
3
|
+
Write your README in markdown, and execute it with specdown.
|
4
|
+
|
5
|
+
## Why?
|
6
|
+
|
7
|
+
Simply put, `specdown` takes README DRIVEN DEVELOPMENT one step further by making your markdown executable.
|
8
|
+
|
9
|
+
If you don't know what README DRIVEN DEVELOPMENT IS, checkout Tom Preston Werner's blog post ["README Driven Development"](http://tom.preston-werner.com/2010/08/23/readme-driven-development.html)
|
10
|
+
|
11
|
+
## CAVEAT
|
12
|
+
|
13
|
+
This library has just been released. It has only a few features, but expect those to grow rapidly over the coming weeks. Follow this repo to stay up to date on the latest changes, or better yet, fork and implement some needed features (see the TODO list at the end of this README).
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
Right now, specdown only support's Ruby. Next, I'll write a javascript implementation. Then, I don't know what language. Regardless, the goal is that you could use specdown with any programming language you desire.
|
18
|
+
|
19
|
+
To install the `specdown` ruby gem, simply:
|
20
|
+
|
21
|
+
$ gem install specdown
|
22
|
+
|
23
|
+
It comes with a `specdown` command. Try running it. Doesn't matter where.
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
Let's write a simple test in markdown, and execute it with specdown. Create a "specdown" directory, then save the following text into a file inside of it. I'll assume you're calling it "example.markdown":
|
28
|
+
|
29
|
+
```markdown
|
30
|
+
# Our first test!
|
31
|
+
|
32
|
+
This is our very first test. It's going to blow your mind.
|
33
|
+
|
34
|
+
raise "WTF?" unless 1 == 1
|
35
|
+
```
|
36
|
+
|
37
|
+
Ok, if you've been following along, then `ls -R` should return the following directory structure:
|
38
|
+
|
39
|
+
$ ls -R
|
40
|
+
|
41
|
+
specdown/
|
42
|
+
example.markdown
|
43
|
+
|
44
|
+
Great. Now run the `specdown` command:
|
45
|
+
|
46
|
+
$ specdown
|
47
|
+
|
48
|
+
.
|
49
|
+
|
50
|
+
1 markdown
|
51
|
+
1 test
|
52
|
+
0 failures
|
53
|
+
|
54
|
+
Booya!
|
55
|
+
|
56
|
+
### How does it work?
|
57
|
+
|
58
|
+
`specdown` loads any "markdown" files it can find inside the "specdown" directory, parses them into trees, then performs exhaustive depth-first searches on the trees to execute the code.
|
59
|
+
|
60
|
+
Let's update our README to help illustrate this:
|
61
|
+
|
62
|
+
```markdown
|
63
|
+
# Our first test!
|
64
|
+
|
65
|
+
This is our very first test. It's going to blow your mind.
|
66
|
+
|
67
|
+
raise "WTF?" unless 1 == 1
|
68
|
+
|
69
|
+
## A Subsection
|
70
|
+
|
71
|
+
In this section, we're going to create a variable.
|
72
|
+
|
73
|
+
name = "moonmaster9000"
|
74
|
+
|
75
|
+
### A sub subsection
|
76
|
+
|
77
|
+
In this subsection, we have access to anything created or within scope in parent sections:
|
78
|
+
|
79
|
+
raise "name not in scope" if !defined? name
|
80
|
+
|
81
|
+
## Another Subsection
|
82
|
+
|
83
|
+
In this subsection, we don't have access to the "name" variable. Think of your markdown as a tree.
|
84
|
+
|
85
|
+
raise "name in scope" if defined? name
|
86
|
+
```
|
87
|
+
|
88
|
+
Read through that. I'm giving you some important scoping hints in it.
|
89
|
+
|
90
|
+
Save it, run it.
|
91
|
+
|
92
|
+
$ specdown
|
93
|
+
|
94
|
+
..
|
95
|
+
|
96
|
+
1 markdown
|
97
|
+
2 tests
|
98
|
+
0 failures
|
99
|
+
|
100
|
+
Notice how the headers in your markdown form a tree?
|
101
|
+
|
102
|
+
#Our first test!
|
103
|
+
/ \
|
104
|
+
/ \
|
105
|
+
/ \
|
106
|
+
/ \
|
107
|
+
/ \
|
108
|
+
/ \
|
109
|
+
/ \
|
110
|
+
##A Subection ##Another Subsection
|
111
|
+
/
|
112
|
+
/
|
113
|
+
/
|
114
|
+
###A sub subsection
|
115
|
+
|
116
|
+
Specdown turned that tree into two tests. The first test (#Our first test! --> ##A Subsection --> ###A sub subsection):
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
raise "WTF?" unless 1 == 1
|
120
|
+
name = "moonmaster9000"
|
121
|
+
raise "name not in scope" if !defined? name
|
122
|
+
```
|
123
|
+
|
124
|
+
Here's what the second test looked like (#Our first test! --> ##Another Subsection)
|
125
|
+
|
126
|
+
```ruby
|
127
|
+
raise "WTF?" unless 1 == 1
|
128
|
+
raise "name in scope" if defined? name
|
129
|
+
```
|
130
|
+
|
131
|
+
## Setting up your test environment
|
132
|
+
|
133
|
+
Similar to the cucumber testing framework: If you put a ruby file somewhere inside your "specdown" directory, `specdown` will find it and load it.
|
134
|
+
|
135
|
+
### Configuring the Expectation / Assertion framework
|
136
|
+
|
137
|
+
As of version 0.1.0, `specdown` supports both RSpec expectations and Test::Unit assertions.
|
138
|
+
|
139
|
+
Specdown will default to RSpec expectations, but if it can't find the "rspec" gem installed on your system, it will fall back to Test::Unit assertions.
|
140
|
+
|
141
|
+
You can also configure `Specdown` manually to use RSpec expectations or Test::Unit assertions.
|
142
|
+
|
143
|
+
#### RSpec expectations
|
144
|
+
|
145
|
+
Create a "support" directory inside your specdown directory, and add an `env.rb` file containing the following Ruby code:
|
146
|
+
|
147
|
+
```ruby
|
148
|
+
Specdown::Config.expectations = :rspec
|
149
|
+
```
|
150
|
+
|
151
|
+
You can now use [RSpec expectations](https://www.relishapp.com/rspec/rspec-expectations) in your tests.
|
152
|
+
|
153
|
+
#### Using Test::Unit::Assertions
|
154
|
+
|
155
|
+
Create a "specdown/support/env.rb" file in your app, then add the following to it:
|
156
|
+
|
157
|
+
```ruby
|
158
|
+
Specdown::Config.expectations = :test_unit
|
159
|
+
```
|
160
|
+
|
161
|
+
You can now use [Test::Unit::Assertions](http://www.ruby-doc.org/stdlib-1.9.3/libdoc/test/unit/rdoc/Test/Unit/Assertions.html) inside your tests.
|
162
|
+
|
163
|
+
## TODO
|
164
|
+
|
165
|
+
This library is the result of about 8 hours worth of work so far :-) It's a basic minimum viable product, but there are tons of features I want to implement. Here's what's on my immediate horizon:
|
166
|
+
|
167
|
+
* Better stack traces / reporting
|
168
|
+
* Test hooks (before/after/around)
|
169
|
+
* Run a single test
|
170
|
+
* color code the terminal output
|
171
|
+
* offer the option of outputing the actual markdown while it executes, instead of "..F....FF......"
|
data/features/command.feature
CHANGED
@@ -18,76 +18,79 @@ Feature: `specdown` command
|
|
18
18
|
|
19
19
|
Given I have a specdown directory containing a single markdown file:
|
20
20
|
"""
|
21
|
-
|
21
|
+
# Specdown Example
|
22
22
|
|
23
|
-
|
23
|
+
This is an example specdown file.
|
24
24
|
|
25
|
-
|
25
|
+
## Child Node
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
This section is a child node. It contains some ruby code:
|
28
|
+
|
29
|
+
"simple code".should_not be(nil)
|
30
30
|
|
31
|
-
|
31
|
+
### First Leaf
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
33
|
+
This section has a failure simulation:
|
34
|
+
|
35
|
+
raise "specdown error simulation!"
|
36
36
|
|
37
|
-
|
37
|
+
## Last Leaf
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
|
39
|
+
This section is a leaf node. It contains some ruby code:
|
40
|
+
|
41
|
+
1.should satisfy(&:odd?)
|
42
42
|
"""
|
43
43
|
|
44
44
|
When I run `specdown` with no arguments
|
45
45
|
Then I should see the following output:
|
46
46
|
"""
|
47
|
+
F.
|
48
|
+
|
47
49
|
1 markdown
|
48
50
|
2 tests
|
49
|
-
|
51
|
+
1 failure
|
50
52
|
|
51
|
-
|
52
|
-
undefined method `should' for 1:Fixnum
|
53
|
+
specdown error simulation
|
53
54
|
"""
|
54
55
|
|
55
56
|
Scenario: `specdown` with no arguments, and a specdown directory containing a single ruby file and a single markdown file
|
56
57
|
Given I have a specdown directory containing a markdown file:
|
57
58
|
"""
|
58
|
-
|
59
|
+
# Specdown Example
|
59
60
|
|
60
|
-
|
61
|
+
This is an example specdown file.
|
61
62
|
|
62
|
-
|
63
|
+
## Child Node
|
63
64
|
|
64
|
-
|
65
|
-
|
66
|
-
|
65
|
+
This section is a child node. It contains some ruby code:
|
66
|
+
|
67
|
+
"simple code".should_not be(nil)
|
67
68
|
|
68
|
-
|
69
|
+
### First Leaf
|
69
70
|
|
70
|
-
|
71
|
-
|
72
|
-
|
71
|
+
This section has a failure simulation:
|
72
|
+
|
73
|
+
raise "specdown error simulation!"
|
73
74
|
|
74
|
-
|
75
|
+
## Last Leaf
|
75
76
|
|
76
|
-
|
77
|
-
|
78
|
-
|
77
|
+
This section is a leaf node. It contains some ruby code:
|
78
|
+
|
79
|
+
1.should satisfy(&:odd?)
|
79
80
|
"""
|
80
81
|
And a single ruby file:
|
81
82
|
"""
|
82
|
-
|
83
|
-
require 'rspec/expectations'
|
83
|
+
Specdown::Config.expectations = :test_unit
|
84
84
|
"""
|
85
85
|
When I run `specdown` with no arguments
|
86
86
|
Then I should see the following output:
|
87
87
|
"""
|
88
|
+
FF
|
89
|
+
|
88
90
|
1 markdown
|
89
91
|
2 tests
|
90
|
-
|
92
|
+
2 failures
|
91
93
|
|
92
|
-
|
94
|
+
In parser_example.markdown: <NoMethodError> undefined method `be'
|
95
|
+
In parser_example.markdown: <NoMethodError> undefined method `satisfy'
|
93
96
|
"""
|
@@ -0,0 +1,96 @@
|
|
1
|
+
Feature: Specdown::Config
|
2
|
+
|
3
|
+
You can use a simple Ruby API to configure specdown.
|
4
|
+
|
5
|
+
Note that at any point, you can reset your Specdown::Config to defaults using the `reset!` method:
|
6
|
+
|
7
|
+
Specdown::Config.reset!
|
8
|
+
Specdown::Config.expectations.should be(nil)
|
9
|
+
|
10
|
+
\## Assertion/Expectation framework
|
11
|
+
|
12
|
+
All of your tests will run within a protected sandbox namespace. If rspec is installed and available, then Specdown will make rspec `should` expectations and matchers availble to your tests. If not, then it will fall back to Test::Unit assertions.
|
13
|
+
|
14
|
+
\### Rspec Expectations
|
15
|
+
|
16
|
+
If you want to explicitly configure `Specdown` to use rspec's expectations, you can use the `expectations` accessor:
|
17
|
+
|
18
|
+
Specdown::Config.expectations = :rspec
|
19
|
+
|
20
|
+
Now, if you instantiate a new sandbox, then rspec's "should" matchers should be available within it:
|
21
|
+
|
22
|
+
Specdown.sandbox.new.instance_eval do
|
23
|
+
1.should_not be(nil)
|
24
|
+
end
|
25
|
+
|
26
|
+
\### Test::Unit assertions
|
27
|
+
|
28
|
+
If you want to explicitly configure `Specdown` to use Test::Unit assertions, set the `expectations` accessor on `Specdown::Config` to `:test_unit`:
|
29
|
+
|
30
|
+
Specdown::Config.expectations = :test_unit
|
31
|
+
|
32
|
+
Now you'll have access to all of the `assert_*` methods within your tests:
|
33
|
+
|
34
|
+
Specdown.sandbox.new.instance_eval do
|
35
|
+
assert_equal 1, 1
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
Scenario: Reset the Specdown::Config
|
40
|
+
|
41
|
+
Given I have configured Specdown:
|
42
|
+
"""
|
43
|
+
Specdown::Config.expectations = :rspec
|
44
|
+
"""
|
45
|
+
|
46
|
+
When I reset Specdown:
|
47
|
+
"""
|
48
|
+
Specdown::Config.reset!
|
49
|
+
"""
|
50
|
+
|
51
|
+
Then my specdown configuration should return to it's defaults:
|
52
|
+
"""
|
53
|
+
Specdown::Config.expectations.should be(nil)
|
54
|
+
"""
|
55
|
+
|
56
|
+
|
57
|
+
Scenario: Default to Rspec expectations
|
58
|
+
|
59
|
+
Given I have rspec installed
|
60
|
+
Then Specdown should provide rspec expectations and matchers within the sandbox by default:
|
61
|
+
"""
|
62
|
+
Specdown.sandbox.instance_eval do
|
63
|
+
1.should_not be(nil)
|
64
|
+
end
|
65
|
+
"""
|
66
|
+
|
67
|
+
Scenario: Manually configure RSpec expectations
|
68
|
+
Given I have manually configured Specdown to use Rspec expectations:
|
69
|
+
"""
|
70
|
+
Specdown::Config.expectations = :rspec
|
71
|
+
"""
|
72
|
+
|
73
|
+
Then Specdown should provide rspec expectations and matchers within the sandbox:
|
74
|
+
"""
|
75
|
+
Specdown.sandbox.instance_eval do
|
76
|
+
1.should_not be(nil)
|
77
|
+
end
|
78
|
+
"""
|
79
|
+
|
80
|
+
Scenario: Manually configure Test::Unit assertions
|
81
|
+
Given I have manually configured Specdown to use Test::Unit assertions:
|
82
|
+
"""
|
83
|
+
Specdown::Config.expectations = :test_unit
|
84
|
+
"""
|
85
|
+
|
86
|
+
Then Specdown should provide Test::Unit assertions within the sandbox:
|
87
|
+
"""
|
88
|
+
Specdown.sandbox.instance_eval do
|
89
|
+
assert_equal 1, 1
|
90
|
+
end
|
91
|
+
"""
|
92
|
+
|
93
|
+
|
94
|
+
|
95
|
+
|
96
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
Feature: Specdown::EventServer
|
2
|
+
|
3
|
+
The Specdown::EventServer receives events and triggers callbacks.
|
4
|
+
|
5
|
+
For example, to send an event to the EventServer, simply give the event a name, then pass any number of arguments after that:
|
6
|
+
|
7
|
+
Specdown::EventServer.event "test passed"
|
8
|
+
|
9
|
+
You can also register callbacks for events using the `register` method:
|
10
|
+
|
11
|
+
Specdown::EventServer.register "test passed" do
|
12
|
+
print "."
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
Scenario: Submitting an event triggers all registered callbacks for that event
|
17
|
+
|
18
|
+
Given a USA population of zero:
|
19
|
+
"""
|
20
|
+
@usa_population = 0
|
21
|
+
"""
|
22
|
+
And a world population of 10:
|
23
|
+
"""
|
24
|
+
@world_population = 10
|
25
|
+
"""
|
26
|
+
And I register callbacks to increment both whenever someone is born:
|
27
|
+
"""
|
28
|
+
Specdown::EventServer.register("birth") {|num_births| @usa_population += num_births }
|
29
|
+
Specdown::EventServer.register("birth") {|num_births| @world_population += num_births }
|
30
|
+
"""
|
31
|
+
When I create a "birth" event:
|
32
|
+
"""
|
33
|
+
Specdown::EventServer.event "birth", 3
|
34
|
+
"""
|
35
|
+
Then both the USA population and the World population should increase:
|
36
|
+
"""
|
37
|
+
@usa_population.should == 3
|
38
|
+
@world_population.should == 13
|
39
|
+
"""
|
@@ -6,7 +6,7 @@ This is an example specdown file.
|
|
6
6
|
|
7
7
|
This section is a child node. It contains some ruby code:
|
8
8
|
|
9
|
-
"simple code".should_not
|
9
|
+
"simple code".should_not be(nil)
|
10
10
|
|
11
11
|
### First Leaf
|
12
12
|
|
@@ -18,4 +18,4 @@ This section has a failure simulation:
|
|
18
18
|
|
19
19
|
This section is a leaf node. It contains some ruby code:
|
20
20
|
|
21
|
-
1.should
|
21
|
+
1.should satisfy(&:odd?)
|
data/features/parser.feature
CHANGED
@@ -6,27 +6,27 @@ Feature: Specdown Parser
|
|
6
6
|
|
7
7
|
readme = <<-README
|
8
8
|
|
9
|
-
|
9
|
+
\# Specdown Example
|
10
10
|
|
11
|
-
|
11
|
+
This is an example specdown file.
|
12
12
|
|
13
|
-
|
13
|
+
\## Child Node
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
This section is a child node. It contains some ruby code:
|
16
|
+
|
17
|
+
"simple code".should_not be(nil)
|
18
18
|
|
19
|
-
|
19
|
+
\### First Leaf
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
This section has a failure simulation:
|
22
|
+
|
23
|
+
raise "specdown error simulation!"
|
24
24
|
|
25
|
-
|
25
|
+
\## Last Leaf
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
This section is a leaf node. It contains some ruby code:
|
28
|
+
|
29
|
+
1.should satisfy(&:odd?)
|
30
30
|
README
|
31
31
|
|
32
32
|
As you can see, this forms a tree, with "# Specdown Example" at the root of the tree, and "## Leaf 1" and "## Leaf 2" as the children / leafs.
|
@@ -55,27 +55,27 @@ Feature: Specdown Parser
|
|
55
55
|
|
56
56
|
Given the following specdown example file:
|
57
57
|
"""
|
58
|
-
|
58
|
+
# Specdown Example
|
59
59
|
|
60
|
-
|
60
|
+
This is an example specdown file.
|
61
61
|
|
62
|
-
|
62
|
+
## Child Node
|
63
63
|
|
64
|
-
|
65
|
-
|
66
|
-
|
64
|
+
This section is a child node. It contains some ruby code:
|
65
|
+
|
66
|
+
"simple code".should_not be(nil)
|
67
67
|
|
68
|
-
|
68
|
+
### First Leaf
|
69
69
|
|
70
|
-
|
71
|
-
|
72
|
-
|
70
|
+
This section has a failure simulation:
|
71
|
+
|
72
|
+
raise "specdown error simulation!"
|
73
73
|
|
74
|
-
|
74
|
+
## Last Leaf
|
75
75
|
|
76
|
-
|
77
|
-
|
78
|
-
|
76
|
+
This section is a leaf node. It contains some ruby code:
|
77
|
+
|
78
|
+
1.should satisfy(&:odd?)
|
79
79
|
"""
|
80
80
|
|
81
81
|
When I parse it into a tree:
|
@@ -98,11 +98,11 @@ Feature: Specdown Parser
|
|
98
98
|
last_leaf = @tree.root.children.last
|
99
99
|
|
100
100
|
child_node.name.should == "Child Node"
|
101
|
-
child_node.code.should == '"simple code".should_not
|
101
|
+
child_node.code.should == '"simple code".should_not be(nil)'
|
102
102
|
|
103
103
|
first_leaf.name.should == "First Leaf"
|
104
104
|
first_leaf.code.should == 'raise "specdown error simulation!"'
|
105
105
|
|
106
106
|
last_leaf.name.should == "Last Leaf"
|
107
|
-
last_leaf.code.should == "1.should
|
107
|
+
last_leaf.code.should == "1.should satisfy(&:odd?)"
|
108
108
|
"""
|
data/features/runner.feature
CHANGED
@@ -2,7 +2,7 @@ Feature: Runner
|
|
2
2
|
|
3
3
|
The `Specdown::Runner` class accepts a markdown parse tree, and runs the tests found within.
|
4
4
|
|
5
|
-
Imagine we start with this markdown file:
|
5
|
+
Imagine we start with this markdown file (saved at "specdown.markdown"):
|
6
6
|
|
7
7
|
\# Specdown Example
|
8
8
|
|
@@ -26,16 +26,12 @@ Feature: Runner
|
|
26
26
|
|
27
27
|
1.should == 1
|
28
28
|
|
29
|
-
We
|
30
|
-
|
31
|
-
parse_tree = Specdown::Parser.parse File.read("/path/to/markdown")
|
32
|
-
|
33
|
-
We can now generate a new `Specdown::Runner` instance and run the tests:
|
29
|
+
We can generate a `Specdown::Runner` instance and run the tests in our markdown by simply passing the filename on instantiation:
|
34
30
|
|
35
|
-
runner = Specdown::Runner.new
|
31
|
+
runner = Specdown::Runner.new "specdown.markdown"
|
36
32
|
runner.run
|
37
33
|
|
38
|
-
While running, it will
|
34
|
+
While running, it will emit events during the processing to the Specdown::EventServer. This enables all kinds of functionality, like printing the progress of the tests.
|
39
35
|
|
40
36
|
We can access statistics about the run programatically:
|
41
37
|
|
@@ -46,7 +42,7 @@ Feature: Runner
|
|
46
42
|
|
47
43
|
Scenario: Running tests
|
48
44
|
|
49
|
-
Given the following specdown example file:
|
45
|
+
Given the following specdown example file located at 'features/fixtures/parser_example.markdown':
|
50
46
|
"""
|
51
47
|
# Specdown Example
|
52
48
|
|
@@ -71,14 +67,9 @@ Feature: Runner
|
|
71
67
|
1.should == 1
|
72
68
|
"""
|
73
69
|
|
74
|
-
When I
|
75
|
-
"""
|
76
|
-
@tree = Specdown::Parser.parse @readme
|
77
|
-
"""
|
78
|
-
|
79
|
-
And I generate a `Specdown::Runner` instance from it:
|
70
|
+
When I generate a `Specdown::Runner` instance from it:
|
80
71
|
"""
|
81
|
-
@runner = Specdown::Runner.new
|
72
|
+
@runner = Specdown::Runner.new "features/fixtures/parser_example.markdown"
|
82
73
|
"""
|
83
74
|
|
84
75
|
Then I should be able to run the tests:
|
@@ -88,8 +79,9 @@ Feature: Runner
|
|
88
79
|
|
89
80
|
And I should be able to access the report data programatically:
|
90
81
|
"""
|
91
|
-
@runner.
|
92
|
-
@runner.stats.
|
93
|
-
@runner.stats.
|
94
|
-
@runner.stats.
|
82
|
+
@runner.file_name.should == 'parser_example.markdown'
|
83
|
+
@runner.stats.tests.should == 2
|
84
|
+
@runner.stats.failures.should == 1
|
85
|
+
@runner.stats.successes.should == 1
|
86
|
+
@runner.stats.exceptions.map(&:to_s).should == ["specdown error simulation!"]
|
95
87
|
"""
|
@@ -6,7 +6,7 @@ This is an example specdown file.
|
|
6
6
|
|
7
7
|
This section is a child node. It contains some ruby code:
|
8
8
|
|
9
|
-
"simple code".should_not
|
9
|
+
"simple code".should_not be(nil)
|
10
10
|
|
11
11
|
### First Leaf
|
12
12
|
|
@@ -18,4 +18,5 @@ This section has a failure simulation:
|
|
18
18
|
|
19
19
|
This section is a leaf node. It contains some ruby code:
|
20
20
|
|
21
|
-
1.should
|
21
|
+
1.should satisfy(&:odd?)
|
22
|
+
|
@@ -1,2 +1 @@
|
|
1
|
-
|
2
|
-
require 'rspec/expectations'
|
1
|
+
Specdown::Config.expectations = :test_unit
|
@@ -6,7 +6,7 @@ This is an example specdown file.
|
|
6
6
|
|
7
7
|
This section is a child node. It contains some ruby code:
|
8
8
|
|
9
|
-
"simple code".should_not
|
9
|
+
"simple code".should_not be(nil)
|
10
10
|
|
11
11
|
### First Leaf
|
12
12
|
|
@@ -18,4 +18,5 @@ This section has a failure simulation:
|
|
18
18
|
|
19
19
|
This section is a leaf node. It contains some ruby code:
|
20
20
|
|
21
|
-
1.should
|
21
|
+
1.should satisfy(&:odd?)
|
22
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
Given /^I have configured Specdown:$/ do |string|
|
2
|
+
eval string
|
3
|
+
end
|
4
|
+
|
5
|
+
When /^I reset Specdown:$/ do |string|
|
6
|
+
eval string
|
7
|
+
end
|
8
|
+
|
9
|
+
Then /^my specdown configuration should return to it's defaults:$/ do |string|
|
10
|
+
eval string
|
11
|
+
end
|
12
|
+
|
13
|
+
Given /^I have rspec installed$/ do
|
14
|
+
end
|
15
|
+
|
16
|
+
Then /^Specdown should provide rspec expectations and matchers within the sandbox by default:$/ do |string|
|
17
|
+
eval string
|
18
|
+
end
|
19
|
+
|
20
|
+
Given /^I have manually configured Specdown to use Rspec expectations:$/ do |string|
|
21
|
+
eval string
|
22
|
+
end
|
23
|
+
|
24
|
+
Then /^Specdown should provide rspec expectations and matchers within the sandbox:$/ do |string|
|
25
|
+
eval string
|
26
|
+
end
|
27
|
+
|
28
|
+
Given /^I have manually configured Specdown to use Test::Unit assertions:$/ do |string|
|
29
|
+
eval string
|
30
|
+
end
|
31
|
+
|
32
|
+
Then /^Specdown should provide Test::Unit assertions within the sandbox:$/ do |string|
|
33
|
+
eval string
|
34
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
Given /^a USA population of zero:$/ do |string|
|
2
|
+
eval string
|
3
|
+
end
|
4
|
+
|
5
|
+
Given /^a world population of .*:$/ do |string|
|
6
|
+
eval string
|
7
|
+
end
|
8
|
+
|
9
|
+
Given /^I register callbacks to increment both whenever someone is born:$/ do |string|
|
10
|
+
eval string
|
11
|
+
end
|
12
|
+
|
13
|
+
When /^I create a ".*" event:$/ do |string|
|
14
|
+
eval string
|
15
|
+
end
|
16
|
+
|
17
|
+
Then /^both the USA population and the World population should increase:$/ do |string|
|
18
|
+
eval string
|
19
|
+
end
|
data/features/support/env.rb
CHANGED
data/lib/specdown/command.rb
CHANGED
@@ -11,14 +11,7 @@ module Specdown
|
|
11
11
|
|
12
12
|
private
|
13
13
|
def run
|
14
|
-
@results =
|
15
|
-
@markdowns.map {|markdown|
|
16
|
-
Parser.parse(File.read(markdown))
|
17
|
-
}.map {|tree|
|
18
|
-
Runner.new(tree)
|
19
|
-
}.map {|runner|
|
20
|
-
runner.run
|
21
|
-
}.collect &:stats
|
14
|
+
@results = @markdowns.map {|markdown| Runner.new(markdown).run.stats}
|
22
15
|
end
|
23
16
|
|
24
17
|
def report
|
File without changes
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Specdown
|
2
|
+
module EventServer
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def event(event_name, *args)
|
6
|
+
callbacks[event_name].map {|callback| callback.call *args} if callbacks[event_name]
|
7
|
+
end
|
8
|
+
|
9
|
+
def register(event_name, &callback)
|
10
|
+
callbacks[event_name] ||= []
|
11
|
+
callbacks[event_name] << callback
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
def callbacks
|
16
|
+
@callbacks ||= {}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/specdown/parser.rb
CHANGED
@@ -34,7 +34,16 @@ module Specdown
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def exceptions
|
37
|
-
|
37
|
+
formatted_exceptions = []
|
38
|
+
@stats.each do |stat|
|
39
|
+
formatted_exceptions << stat.exceptions.map do |e|
|
40
|
+
[
|
41
|
+
(stat.runner ? "In #{stat.runner.file_name}: " : "") + "<#{e.class}> #{e}",
|
42
|
+
e.backtrace
|
43
|
+
].join "\n"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
formatted_exceptions.flatten
|
38
47
|
end
|
39
48
|
end
|
40
49
|
end
|
data/lib/specdown/runner.rb
CHANGED
@@ -1,15 +1,20 @@
|
|
1
1
|
module Specdown
|
2
2
|
class Runner
|
3
|
-
attr_reader :stats
|
3
|
+
attr_reader :stats, :file_path
|
4
4
|
|
5
|
-
def initialize(
|
6
|
-
@
|
7
|
-
@
|
5
|
+
def initialize(file_path)
|
6
|
+
@file_path = file_path
|
7
|
+
@tree = Parser.parse File.read(file_path)
|
8
|
+
@stats = Stats.new self
|
9
|
+
end
|
10
|
+
|
11
|
+
def file_name
|
12
|
+
File.basename @file_path
|
8
13
|
end
|
9
14
|
|
10
15
|
def run
|
11
16
|
depth_first_search @tree.root
|
12
|
-
|
17
|
+
EventServer.event :run_complete
|
13
18
|
self
|
14
19
|
end
|
15
20
|
|
@@ -28,16 +33,17 @@ module Specdown
|
|
28
33
|
@stats.tests += 1
|
29
34
|
|
30
35
|
begin
|
31
|
-
|
32
|
-
|
33
|
-
|
36
|
+
Specdown.sandbox.instance_eval <<-CODE, file_name
|
37
|
+
#{code.join("\n")}
|
38
|
+
CODE
|
34
39
|
|
35
|
-
|
40
|
+
EventServer.event :test_passed
|
36
41
|
|
37
42
|
rescue Exception => e
|
38
43
|
@stats.exceptions << e
|
39
44
|
|
40
|
-
|
45
|
+
EventServer.event :test_failed
|
46
|
+
|
41
47
|
end
|
42
48
|
end
|
43
49
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Specdown
|
2
|
+
module SandboxFactory
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def generate
|
6
|
+
Module.new {}.tap do |sandbox|
|
7
|
+
setup_expectation_library.call(sandbox)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
def setup_expectation_library
|
13
|
+
expectation_library_setups[Config.expectations]
|
14
|
+
end
|
15
|
+
|
16
|
+
def expectation_library_setups
|
17
|
+
Hash.new(default_expectations_setup).tap do |setups|
|
18
|
+
setups[:rspec] = rspec_expectations_setup
|
19
|
+
setups[:test_unit] = test_unit_expectations_setup
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def rspec_installed?
|
24
|
+
require_rspec
|
25
|
+
end
|
26
|
+
|
27
|
+
def require_rspec
|
28
|
+
begin
|
29
|
+
require('rspec/expectations')
|
30
|
+
true
|
31
|
+
rescue LoadError
|
32
|
+
false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def default_expectations_setup
|
37
|
+
if rspec_installed?
|
38
|
+
rspec_expectations_setup
|
39
|
+
else
|
40
|
+
test_unit_expectations_setup
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def rspec_expectations_setup
|
45
|
+
require_rspec
|
46
|
+
proc {|sandbox| sandbox.extend ::RSpec::Matchers}
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_unit_expectations_setup
|
50
|
+
proc do |sandbox|
|
51
|
+
require 'test/unit/assertions'
|
52
|
+
sandbox.extend ::Test::Unit::Assertions
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/specdown.rb
CHANGED
@@ -2,8 +2,15 @@ require 'kramdown'
|
|
2
2
|
require 'specdown/parser'
|
3
3
|
require 'specdown/node'
|
4
4
|
require 'specdown/tree'
|
5
|
-
require 'specdown/sandbox'
|
6
5
|
require 'specdown/runner'
|
7
6
|
require 'specdown/runner/report'
|
8
7
|
require 'specdown/runner/stats'
|
9
8
|
require 'specdown/command'
|
9
|
+
require 'specdown/event_server'
|
10
|
+
require 'specdown/event_handlers/run_complete'
|
11
|
+
require 'specdown/event_handlers/run_started'
|
12
|
+
require 'specdown/event_handlers/test_failed'
|
13
|
+
require 'specdown/event_handlers/test_passed'
|
14
|
+
require 'specdown/config'
|
15
|
+
require 'specdown/specdown'
|
16
|
+
require 'specdown/sandbox_factory'
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: specdown
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 27
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
- 0
|
9
8
|
- 1
|
10
|
-
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Matt Parker
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
18
|
+
date: 2012-01-01 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: kramdown
|
@@ -68,22 +68,31 @@ executables:
|
|
68
68
|
extensions: []
|
69
69
|
|
70
70
|
extra_rdoc_files:
|
71
|
+
- CHANGELOG.markdown
|
71
72
|
- README.markdown
|
72
|
-
- SPEC.markdown
|
73
73
|
files:
|
74
74
|
- lib/specdown/command.rb
|
75
|
+
- lib/specdown/config.rb
|
76
|
+
- lib/specdown/event_handlers/run_complete.rb
|
77
|
+
- lib/specdown/event_handlers/run_started.rb
|
78
|
+
- lib/specdown/event_handlers/test_failed.rb
|
79
|
+
- lib/specdown/event_handlers/test_passed.rb
|
80
|
+
- lib/specdown/event_server.rb
|
75
81
|
- lib/specdown/node.rb
|
76
82
|
- lib/specdown/parser.rb
|
77
83
|
- lib/specdown/runner/report.rb
|
78
84
|
- lib/specdown/runner/stats.rb
|
79
85
|
- lib/specdown/runner.rb
|
80
|
-
- lib/specdown/
|
86
|
+
- lib/specdown/sandbox_factory.rb
|
87
|
+
- lib/specdown/specdown.rb
|
81
88
|
- lib/specdown/tree.rb
|
82
89
|
- lib/specdown.rb
|
83
90
|
- bin/specdown
|
91
|
+
- CHANGELOG.markdown
|
84
92
|
- README.markdown
|
85
|
-
- SPEC.markdown
|
86
93
|
- features/command.feature
|
94
|
+
- features/config.feature
|
95
|
+
- features/event_server.feature
|
87
96
|
- features/fixtures/parser_example.markdown
|
88
97
|
- features/parser.feature
|
89
98
|
- features/report.feature
|
@@ -92,10 +101,13 @@ files:
|
|
92
101
|
- features/specdown_examples/with_ruby/specdown/env.rb
|
93
102
|
- features/specdown_examples/with_ruby/specdown/parser_example.markdown
|
94
103
|
- features/step_definitions/command.rb
|
104
|
+
- features/step_definitions/config.rb
|
105
|
+
- features/step_definitions/event_server.rb
|
95
106
|
- features/step_definitions/parser.rb
|
96
107
|
- features/step_definitions/report.rb
|
97
108
|
- features/step_definitions/runner.rb
|
98
109
|
- features/support/env.rb
|
110
|
+
- features/support/hooks.rb
|
99
111
|
homepage: http://github.com/moonmaster9000/specdown
|
100
112
|
licenses: []
|
101
113
|
|
@@ -131,6 +143,8 @@ specification_version: 3
|
|
131
143
|
summary: Write your specs as if they were a README, then EXECUTE them.
|
132
144
|
test_files:
|
133
145
|
- features/command.feature
|
146
|
+
- features/config.feature
|
147
|
+
- features/event_server.feature
|
134
148
|
- features/fixtures/parser_example.markdown
|
135
149
|
- features/parser.feature
|
136
150
|
- features/report.feature
|
@@ -139,7 +153,10 @@ test_files:
|
|
139
153
|
- features/specdown_examples/with_ruby/specdown/env.rb
|
140
154
|
- features/specdown_examples/with_ruby/specdown/parser_example.markdown
|
141
155
|
- features/step_definitions/command.rb
|
156
|
+
- features/step_definitions/config.rb
|
157
|
+
- features/step_definitions/event_server.rb
|
142
158
|
- features/step_definitions/parser.rb
|
143
159
|
- features/step_definitions/report.rb
|
144
160
|
- features/step_definitions/runner.rb
|
145
161
|
- features/support/env.rb
|
162
|
+
- features/support/hooks.rb
|
data/SPEC.markdown
DELETED
@@ -1,122 +0,0 @@
|
|
1
|
-
# specdown
|
2
|
-
|
3
|
-
Write your README in markdown, and execute it with specdown. Documentation == Specification == WINNING
|
4
|
-
|
5
|
-
## Why?
|
6
|
-
|
7
|
-
I love README DRIVEN DEVELOPMENT. I love the free-form nature of writing a README. I dislike the process of copying and transforming my README into cucumber. Gherkin works well for many types of requirements, but I dislike turning readable, natural prose into "Given/When/Then" Gherkin. I dislike turning my readable prose into RSpec even more.
|
8
|
-
|
9
|
-
## How?
|
10
|
-
|
11
|
-
Writing your tests with specdown is as simple as writing a README. I'll show you.
|
12
|
-
|
13
|
-
Let's imagine we're writing a README for a silly little ruby library called "Todo":
|
14
|
-
|
15
|
-
# Todo
|
16
|
-
|
17
|
-
A simple library for managing your todo list.
|
18
|
-
|
19
|
-
## Usage
|
20
|
-
|
21
|
-
Go into `irb`, then `require 'todo'`. Type `Todo.List` to see your current to do list:
|
22
|
-
|
23
|
-
Todo.List #==> []
|
24
|
-
|
25
|
-
If at any point, you want to completely reset your Todo.List, simply call `Todo!`:
|
26
|
-
|
27
|
-
Todo!
|
28
|
-
Todo.List.should be_empty
|
29
|
-
|
30
|
-
### Adding/Removing
|
31
|
-
|
32
|
-
Now, add an item to your to-do list by calling it as if it were a method on the `Todo` object:
|
33
|
-
|
34
|
-
Todo.shop_for_groceries
|
35
|
-
|
36
|
-
This added the item to your `Todo.List`:
|
37
|
-
|
38
|
-
Todo.List.should == [:shop_for_groceries]
|
39
|
-
|
40
|
-
You can remove an item from your todo list by adding an exclamation point onto the end of it:
|
41
|
-
Todo.shop_for_groceries!
|
42
|
-
Todo.List.should be_empty
|
43
|
-
|
44
|
-
|
45
|
-
### Bulk operations
|
46
|
-
|
47
|
-
You can also perform bulk operations on your `Todo.List`:
|
48
|
-
|
49
|
-
Todo.edit! do
|
50
|
-
shop
|
51
|
-
dry_cleaning!
|
52
|
-
shave!
|
53
|
-
walk_dogs
|
54
|
-
end
|
55
|
-
|
56
|
-
The items "dry_cleaning" and "shave" were removed from your `Todo.List`, and the items "shop" and "walk_dogs" were added to your Todo.List.
|
57
|
-
|
58
|
-
Todo.List.should == [
|
59
|
-
:shop,
|
60
|
-
:walk_dogs
|
61
|
-
]
|
62
|
-
|
63
|
-
Let's call this file "README.markdown", and place it inside a "specdown/" directory:
|
64
|
-
|
65
|
-
$ ls -r
|
66
|
-
specdown/
|
67
|
-
README.markdown
|
68
|
-
|
69
|
-
We can execute this markdown with specdown by simply the `specdown` command. If you'd like to have some code run before the markdown is executed, put it in a ruby file inside a "specdown" directory:
|
70
|
-
|
71
|
-
$ cat > specdown/support.rb
|
72
|
-
|
73
|
-
$LOAD_PATH.unshift './lib'
|
74
|
-
require 'rspec/expectations' # for using "should" matchers in the README
|
75
|
-
require 'todo'
|
76
|
-
|
77
|
-
## Trees and Leafs
|
78
|
-
|
79
|
-
In the README that we wrote for `Todo`, we actually wrote two tests, or scenarios.
|
80
|
-
|
81
|
-
`specdown` creates a tree out of our markdown. In our case, the tree for our `Todo` README looks like this:
|
82
|
-
|
83
|
-
|
84
|
-
#Todo
|
85
|
-
|
|
86
|
-
##Usage
|
87
|
-
/ \
|
88
|
-
/ \
|
89
|
-
/ \
|
90
|
-
/ \
|
91
|
-
/ \
|
92
|
-
/ \
|
93
|
-
/ \
|
94
|
-
###Adding/Removing ###Bulk Operations
|
95
|
-
|
96
|
-
`specdown` will then walk the path from the root to every leaf node in the tree, executing any code it finds along the way.
|
97
|
-
|
98
|
-
Thus, the first "test" will look like this:
|
99
|
-
|
100
|
-
Todo.List #==> []
|
101
|
-
Todo!
|
102
|
-
Todo.List.should be_empty
|
103
|
-
Todo.shop_for_groceries
|
104
|
-
Todo.List.should == [:shop_for_groceries]
|
105
|
-
Todo.shop_for_groceries!
|
106
|
-
Todo.List.should be_empty
|
107
|
-
|
108
|
-
The second "test" looks like this:
|
109
|
-
|
110
|
-
Todo.List
|
111
|
-
Todo!
|
112
|
-
Todo.List.should be_empty
|
113
|
-
Todo.edit! do
|
114
|
-
shop
|
115
|
-
dry_cleaning!
|
116
|
-
shave!
|
117
|
-
walk_dogs
|
118
|
-
end
|
119
|
-
Todo.List.should == [
|
120
|
-
:shop,
|
121
|
-
:walk_dogs
|
122
|
-
]
|
data/lib/specdown/sandbox.rb
DELETED