riot 0.10.11 → 0.10.12.pre
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -1
- data/CHANGELOG +34 -0
- data/README.markdown +81 -456
- data/Rakefile +31 -28
- data/VERSION +1 -1
- data/lib/riot.rb +2 -1
- data/lib/riot/assertion.rb +1 -1
- data/lib/riot/assertion_macro.rb +51 -5
- data/lib/riot/assertion_macros/any.rb +1 -1
- data/lib/riot/assertion_macros/assigns.rb +2 -2
- data/lib/riot/assertion_macros/empty.rb +1 -1
- data/lib/riot/assertion_macros/equals.rb +4 -4
- data/lib/riot/assertion_macros/equivalent_to.rb +22 -0
- data/lib/riot/assertion_macros/includes.rb +2 -2
- data/lib/riot/assertion_macros/kind_of.rb +2 -2
- data/lib/riot/assertion_macros/matches.rb +2 -2
- data/lib/riot/assertion_macros/nil.rb +1 -1
- data/lib/riot/assertion_macros/raises.rb +7 -10
- data/lib/riot/assertion_macros/respond_to.rb +3 -2
- data/lib/riot/assertion_macros/same_elements.rb +1 -1
- data/lib/riot/assertion_macros/size.rb +2 -2
- data/lib/riot/context.rb +104 -19
- data/lib/riot/message.rb +23 -0
- data/lib/riot/rr.rb +35 -0
- data/lib/riot/runnable.rb +12 -0
- data/lib/riot/situation.rb +4 -0
- data/riot.gemspec +20 -4
- data/test/assertion_macro_test.rb +30 -0
- data/test/assertion_macros/assigns_test.rb +3 -3
- data/test/assertion_macros/equals_test.rb +2 -9
- data/test/assertion_macros/equivalent_to_test.rb +36 -0
- data/test/assertion_macros/respond_to_test.rb +1 -0
- data/test/assertion_macros/size_test.rb +6 -6
- data/test/assertion_test.rb +1 -1
- data/test/benchmark/message_concatenation.rb +82 -0
- data/test/context_test.rb +17 -2
- data/test/extensions/rrriot_test.rb +69 -0
- data/test/message_test.rb +35 -0
- data/test/teststrap.rb +1 -1
- metadata +27 -4
data/.gitignore
CHANGED
data/CHANGELOG
CHANGED
@@ -1,3 +1,37 @@
|
|
1
|
+
*0.10.12.pre*
|
2
|
+
|
3
|
+
* RR support in Riot [vandrijevik,jaknowlden]
|
4
|
+
|
5
|
+
# teststrap.rb
|
6
|
+
require 'riot'
|
7
|
+
Riot.rr
|
8
|
+
|
9
|
+
# your-test.rb
|
10
|
+
context "foo" do
|
11
|
+
setup do
|
12
|
+
mock!.hello {"world"}
|
13
|
+
end
|
14
|
+
|
15
|
+
asserts("failure due to not calling hello") { true } # actually fails
|
16
|
+
end
|
17
|
+
|
18
|
+
* Added Riot::Message to make messages in macros easier to write [jaknowlden]
|
19
|
+
|
20
|
+
def evaluate(actual, expected)
|
21
|
+
# ...
|
22
|
+
expected == actual pass(new_message.received(expected)) ? fail(expected(expected).not(actual))
|
23
|
+
# ...
|
24
|
+
end
|
25
|
+
|
26
|
+
* Added responds_to as a respond_to alias [jaknowlden]
|
27
|
+
|
28
|
+
* Added the equivalent_to macro to compare case equality (===). equals is now (==) [jaknowlden]
|
29
|
+
|
30
|
+
* Assuming RootContext if nil parent provided. Added Context#parent to the API [jaknowlden]
|
31
|
+
|
32
|
+
Riot::Context.new("Hi", nil) {}.parent.class
|
33
|
+
=> Riot::RootContext
|
34
|
+
|
1
35
|
*0.10.11*
|
2
36
|
|
3
37
|
* Context#asserts_topic now takes an optional description [gabrielg, jaknowlden]
|
data/README.markdown
CHANGED
@@ -1,509 +1,134 @@
|
|
1
1
|
# Riot
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
## Contents
|
6
|
-
|
7
|
-
- [Installation](#installation)
|
8
|
-
- [Note on speed](#speed)
|
9
|
-
- [Examples](#examples)
|
10
|
-
- [Boolean](#examples-boolean)
|
11
|
-
- [Shortcuts](#examples-shortcut)
|
12
|
-
- [Equality](#examples-equality)
|
13
|
-
- [Matches](#examples-matches)
|
14
|
-
- [Raises](#examples-raises)
|
15
|
-
- [Kind Of](#examples-kind-of)
|
16
|
-
- [Empty](#examples-empty)
|
17
|
-
- [Respond To](#examples-respond-to)
|
18
|
-
- [Assigns](#examples-assigns)
|
19
|
-
- [Nested contexts](#examples-nested)
|
20
|
-
- [Block expectations](#examples-block)
|
21
|
-
- [OMG! Why did you write this](#omg)
|
22
|
-
- [Running tests](#running)
|
23
|
-
- [With Sinatra](#sinatra)
|
24
|
-
- [With Rails](#rails)
|
25
|
-
- [Extending Riot with Macros](#extending)
|
26
|
-
- [Assertion macros](#assertion-macros)
|
27
|
-
|
28
|
-
When you're done reading here, take a gander at:
|
29
|
-
|
30
|
-
* [Riot Rails](http://github.com/thumblemonks/riot_rails): Rails support for Riot testing. A definite work in progress (help welcomed). See [evoke-app](http://github.com/thumblemonks/evoke-app) for live examples.
|
31
|
-
* [Riot.js](http://github.com/alexyoung/riotjs): for you JavaScript people, a version of Riot just for you. Both implementations will be informing the other. Lots of stuff to learn.
|
32
|
-
|
33
|
-
<a name="installation"></a>
|
34
|
-
#### Installation
|
35
|
-
|
36
|
-
The Riot gem is hosted on gemcutter.org. It used to be hosted on GitHub, but they turned off gem support while moving to Rackspace, so we moved to gemcutter. If you have not already done so, add gemcutter.org to your list of gem sources like so:
|
3
|
+
A fast, expressive and concise unit-testing framework.
|
37
4
|
|
38
|
-
sudo gem sources -a http://gemcutter.org
|
39
|
-
|
40
|
-
Then, simply install the Riot gem like so:
|
41
|
-
|
42
|
-
sudo gem install riot
|
43
|
-
|
44
|
-
<a name="speed"></a>
|
45
|
-
#### Note on speed
|
46
|
-
|
47
|
-
I have done a really simple benchmarking (10,000 runs), but right now, Riot is running about **3 times** faster than Test::Unit and thusly Shoulda:
|
48
|
-
|
49
|
-
Rehearsal ----------------------------------------------
|
50
|
-
Riot 0.360000 0.000000 0.360000 ( 0.364236)
|
51
|
-
Test::Unit 1.250000 0.000000 1.250000 ( 1.258466)
|
52
|
-
Shoulda 1.270000 0.010000 1.280000 ( 1.277429)
|
53
|
-
------------------------------------- total: 2.890000sec
|
54
|
-
|
55
|
-
user system total real
|
56
|
-
Riot 0.360000 0.000000 0.360000 ( 0.353360)
|
57
|
-
Test::Unit 1.260000 0.000000 1.260000 ( 1.263777)
|
58
|
-
Shoulda 1.270000 0.000000 1.270000 ( 1.270957)
|
59
|
-
|
60
|
-
"Is it valid?", you ask. *I* think it is. I ain't no cheater, but I might be delusional.
|
61
|
-
|
62
|
-
To compare against MiniTest, I had to run the benchmark separately.
|
63
|
-
|
64
|
-
Rehearsal --------------------------------------------
|
65
|
-
Riot 0.350000 0.000000 0.350000 ( 0.354331)
|
66
|
-
MiniTest 0.720000 0.070000 0.790000 ( 0.793093)
|
67
|
-
----------------------------------- total: 1.140000sec
|
68
|
-
|
69
|
-
user system total real
|
70
|
-
Riot 0.350000 0.000000 0.350000 ( 0.348796)
|
71
|
-
MiniTest 0.730000 0.060000 0.790000 ( 0.801629)
|
72
|
-
|
73
|
-
Riot is currently about twice as fast as minitest. Riot is also half the code of MiniTest (`310 loc < 674 loc` :)
|
74
|
-
|
75
|
-
All tests ran with `ruby 1.8.7 (2009-06-12 patchlevel 174) [i686-darwin9.8.0], MBARI 0x8770, Ruby Enterprise Edition 2009.10`.
|
76
|
-
|
77
|
-
Riot also works very well with Ruby 1.9. The same benchmarks from above run through ruby-1.9 show Riot to be twice as fast as it is already. See our [benchmarks gist](http://gist.github.com/240353) for more details.
|
78
|
-
|
79
|
-
<a name="examples"></a>
|
80
|
-
## Examples
|
81
|
-
|
82
|
-
<a name="examples-boolean"></a>
|
83
|
-
#### Example: Basic booleans
|
84
|
-
|
85
|
-
**NOTE:** For no specific reason, I'm going to use an ActiveRecord model in the following examples.
|
86
|
-
|
87
|
-
At it's very basic, Riot simply tries to assert that an expression is true or false. Riot does this through its `asserts` or `should` tests. An `asserts` test will pass if the result of running the test is neither `nil` or `false`. A `should` test does the exact same thing - they are in effect, aliases. The only difference is in the way you write the assertion.
|
88
|
-
|
89
|
-
For instance, given a test file named `foo_test.rb`, you might have the following code in it:
|
90
|
-
|
91
|
-
require 'riot'
|
92
|
-
|
93
|
-
context "a new user" do
|
94
|
-
setup { User.new }
|
95
|
-
asserts("that it is not yet created") { topic.new_record? }
|
96
|
-
teardown { #cleanup here }
|
97
|
-
end
|
98
|
-
|
99
|
-
Notice that you do not define a class anywhere. That would be the entire contents of that test file. If you wanted to use a `should` instead, you could say this:
|
100
|
-
|
101
|
-
require 'riot'
|
102
|
-
|
103
|
-
context "a new user" do
|
104
|
-
setup { User.new }
|
105
|
-
should("not be created") { topic.new_record? }
|
106
|
-
end
|
107
|
-
|
108
|
-
Sometimes it's more clear to say "this **should** be that" and sometimes it's better to say "**asserts** this is that". I promise you that Riot will get no more redundant than this, but also that besides speed, Riot will aim at being expressive with a minimal amount of syntax.
|
109
|
-
|
110
|
-
The other important thing to note in the examples above is the use of the `topic`. Calling `topic` within any assertion will actually return the value of whatever was evaluated and returned from calling the last setup in the given context. In the examples above, `User.new` was returned, and is therefor accessible as the `topic`.
|
111
|
-
|
112
|
-
I'm going to use `asserts` for the rest of this introduction, but you should know that you can replace any instance of `asserts` with `should` and nothing would change.
|
113
5
|
|
114
|
-
|
115
|
-
#### Example: Shortcuts
|
6
|
+
## Installation
|
116
7
|
|
117
|
-
|
118
|
-
Over the course of developing Riot it became somewhat obvious to some of us that we were creating assertions that returned the `topic` just so we could assert things about the topic itself. For instance, were doing this:
|
8
|
+
Add Gemcutter to your gem sources:
|
119
9
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
should("be a Billionaire") { topic }.kind_of(Billionaire)
|
124
|
-
end
|
125
|
-
|
126
|
-
This is awfully redundant - not to mention, contrived. So, we wrote a shortcut to generate an assertion that returns topic. This means we can now do this:
|
127
|
-
|
128
|
-
context "a billionaire" do
|
129
|
-
setup { MoneyMaker.build(:billionaire) }
|
130
|
-
asserts_topic.kind_of(Billionaire)
|
131
|
-
end
|
132
|
-
|
133
|
-
If you'd like to add a description to your assertion about the topic, but still would like to avoid the redundancy of an assertion simply returning the topic from it's block, you can omit the block all together as follows:
|
134
|
-
|
135
|
-
context "a trazillionaire" do
|
136
|
-
setup { MoneyMaker.build(:trazillionaire) }
|
137
|
-
asserts("is a trazillionaire").kind_of(Trazillionaire)
|
138
|
-
end
|
139
|
-
|
140
|
-
##### Asserting methods on topic
|
141
|
-
|
142
|
-
A very common assertion pattern with Riot is calling a method on the topic and asserting it's output. For instance, the following is not an uncommon thing to see in an `ActiveRecord` test:
|
143
|
-
|
144
|
-
context "A new post" do
|
145
|
-
setup { Post.new(:name => "Howdy ya'll") }
|
146
|
-
asserts("name") { topic.name }.equals("Howdy ya'll")
|
147
|
-
end
|
148
|
-
|
149
|
-
Since Riot is all about reducing redundancy and increasing semantics, it makes a ton of sense to be able to do this instead:
|
150
|
-
|
151
|
-
context "A new post" do
|
152
|
-
setup { Post.new(:name => "Howdy ya'll") }
|
153
|
-
asserts(:name).equals("Howdy ya'll")
|
154
|
-
end
|
155
|
-
|
156
|
-
And now you can! Isn't that just so much easier to read?
|
157
|
-
|
158
|
-
<a name="examples-equality"></a>
|
159
|
-
#### Example: Equality
|
160
|
-
|
161
|
-
One of the most common assertions you will (or do already) utilize is that of equality; is this equal to that? Riot supports this in a slightly different manner than most other frameworks. With Riot, you add the expectation to the assertion itself.
|
162
|
-
|
163
|
-
For example:
|
164
|
-
|
165
|
-
context "a new user" do
|
166
|
-
setup { User.new(:email => 'foo@bar.com') }
|
167
|
-
asserts("email address") { topic.email }.equals('foo@bar.com')
|
168
|
-
end
|
169
|
-
|
170
|
-
Here, you should begin to notice that tests themselves return the actual value. You do not write assertions into the test. Assertions are "aspected" onto the test. If the test above did not return 'foo@bar.com' for `topic.email`, the assertion would have failed.
|
171
|
-
|
172
|
-
The `equals` modifier works with any type of value, including nil's. However, if you wanted to test for nil explicitly, you could simply do this:
|
173
|
-
|
174
|
-
context "a new user" do
|
175
|
-
setup { User.new }
|
176
|
-
asserts("email address") { topic.email }.nil
|
177
|
-
end
|
178
|
-
|
179
|
-
Notice the `nil` modifier added to asserts. Also notice how the test almost reads as "a new user asserts email address *is* nil". With Test::Unit, you would have probably written:
|
180
|
-
|
181
|
-
class UserTest < Test::Unit::TestCase
|
182
|
-
def setup
|
183
|
-
@user = User.new
|
184
|
-
end
|
185
|
-
|
186
|
-
def test_email_address_is_nil
|
187
|
-
assert_nil @user.email
|
188
|
-
end
|
189
|
-
end
|
190
|
-
|
191
|
-
Which, to me, seems like a redundancy. The test already says it's nil! Maybe Shoulda woulda helped:
|
192
|
-
|
193
|
-
class UserTest < Test::Unit::TestCase
|
194
|
-
def setup
|
195
|
-
@user = User.new
|
196
|
-
end
|
197
|
-
|
198
|
-
should "have nil email" { assert_nil @user.email }
|
199
|
-
end
|
200
|
-
|
201
|
-
In my opinion, the same redundancy exists. Sure, I could write a macro like `should_be_nil {@user.email}`, but the redundancy exists in the framework itself.
|
202
|
-
|
203
|
-
<a name="examples-matches"></a>
|
204
|
-
#### Example: Matches
|
205
|
-
|
206
|
-
If you need to assert that a test result matches a regular expression, use the `matches` modifier like so:
|
207
|
-
|
208
|
-
context "a new user" do
|
209
|
-
setup { User.new }
|
210
|
-
|
211
|
-
# I'm a contrived example
|
212
|
-
asserts("random phone number") { topic.random_phone_number }.matches(/^\d{3}-\d{3}-\d{4}$/)
|
213
|
-
end
|
214
|
-
|
215
|
-
<a name="examples-raises"></a>
|
216
|
-
#### Example: Raises
|
217
|
-
|
218
|
-
Sometimes, your test raises an exception that you actually expected.
|
219
|
-
|
220
|
-
context "a new user" do
|
221
|
-
setup { User.new }
|
222
|
-
asserts("invalid data") { topic.save! }.raises(ActiveRecord::RecordInvalid)
|
223
|
-
end
|
224
|
-
|
225
|
-
And if you wanted to check that the exception and message match what you expect:
|
226
|
-
|
227
|
-
context "a new user" do
|
228
|
-
setup { User.new }
|
229
|
-
asserts("invalid data") { topic.save! }.raises(ActiveRecord::RecordInvalid, /has errors/)
|
230
|
-
end
|
231
|
-
|
232
|
-
<a name="examples-kind-of"></a>
|
233
|
-
#### Example: Kind Of
|
234
|
-
|
235
|
-
When you want to test that an expression returns an object of an expected type:
|
236
|
-
|
237
|
-
context "a new user" do
|
238
|
-
setup { User.new }
|
239
|
-
asserts("its balance") { topic.balance }.kind_of(Currency)
|
240
|
-
end
|
241
|
-
|
242
|
-
<a name="examples-empty"></a>
|
243
|
-
#### Example: Empty
|
244
|
-
|
245
|
-
When you want to assert a test result is empty (length == 0):
|
246
|
-
|
247
|
-
context "a new user" do
|
248
|
-
setup { User.new }
|
249
|
-
should("have no first name") { topic.first_name }.empty
|
250
|
-
|
251
|
-
#let's imagine phone numbers is an array
|
252
|
-
should("have no phone numbers") { topic.phone_numbers }.empty
|
253
|
-
|
254
|
-
#let's imagine attributes is a hash
|
255
|
-
should("have no attributes") { topic.attributes }.empty
|
256
|
-
end
|
257
|
-
|
258
|
-
<a name="examples-respond-to"></a>
|
259
|
-
#### Example: Respond To
|
10
|
+
!!!plain
|
11
|
+
sudo gem sources -a http://gemcutter.org
|
260
12
|
|
261
|
-
|
13
|
+
Then, simply install the Riot gem like so:
|
262
14
|
|
263
|
-
|
264
|
-
|
265
|
-
asserts("email is defined") { topic }.respond_to(:email)
|
266
|
-
end
|
15
|
+
!!!plain
|
16
|
+
sudo gem install riot
|
267
17
|
|
268
|
-
<a name="examples-assigns"></a>
|
269
|
-
#### Example: Assigns
|
270
18
|
|
271
|
-
|
19
|
+
## Usage
|
272
20
|
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
asserts("has a first name") { topic }.assigns(:first_name) # This will fail
|
279
|
-
end
|
21
|
+
In contrast to other popular Ruby testing frameworks such as Test::Unit,
|
22
|
+
[Shoulda](http://github.com/thoughtbot/shoulda) and [RSpec](http://rspec.info/),
|
23
|
+
Riot does not run a `setup` and `teardown` sequence before and after each test. This speeds
|
24
|
+
up the test runs quite a bit, but also puts restrictions on how you write your tests. In
|
25
|
+
general, you should avoid mutating any objects under test.
|
280
26
|
|
281
|
-
|
27
|
+
In Riot, tests reside in *contexts*. Within these, a *topic* object is defined through a
|
28
|
+
`setup` block. The actual assertions are then made with `assert`.
|
282
29
|
|
283
|
-
context "
|
284
|
-
setup
|
285
|
-
|
286
|
-
end
|
287
|
-
asserts("has an email address") { topic }.assigns(:email, "foo@bar.baz")
|
288
|
-
asserts("has a first name") { topic }.assigns(:first_name, "Joe") # This will fail
|
30
|
+
context "An Array" do
|
31
|
+
setup { Array.new }
|
32
|
+
asserts("is empty") { topic.empty? }
|
289
33
|
end
|
290
34
|
|
291
|
-
|
292
|
-
|
35
|
+
As you can see, the setup block doesn't use any instance variables to save the object under
|
36
|
+
test -- rather, the return value of the block is used as the *topic*. This object can then
|
37
|
+
be accessed in the assertions using the `topic` attribute.
|
293
38
|
|
294
|
-
|
39
|
+
Furthermore, assertions need only return a boolean; `true` indicates a pass, while `false`
|
40
|
+
indicates a fail.
|
295
41
|
|
296
|
-
|
297
|
-
setup { @app = MyApp }
|
42
|
+
Contexts can also be nested; the `setup` blocks are executed outside-in before.
|
298
43
|
|
299
|
-
|
300
|
-
|
301
|
-
# ...
|
302
|
-
# assertions
|
44
|
+
context "An Array" do
|
45
|
+
setup { Array.new }
|
303
46
|
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
end
|
309
|
-
end
|
310
|
-
|
311
|
-
context "get /books/1" do
|
312
|
-
setup { get '/books/1' }
|
313
|
-
# ...
|
314
|
-
# assertions
|
47
|
+
context "with one element" do
|
48
|
+
setup { topic << "foo" }
|
49
|
+
asserts("is not empty") { !topic.empty? }
|
50
|
+
asserts("returns the element on #first") { topic.first == "foo" }
|
315
51
|
end
|
316
52
|
end
|
317
53
|
|
318
|
-
<a name="examples-block"></a>
|
319
|
-
#### Example: Block expectations
|
320
|
-
|
321
|
-
You can use a block to create the expected value for your assertion,
|
322
|
-
which might come in useful if it takes a bit of work to generate your
|
323
|
-
expected value. My apologies for the incredibly contrived example as so:
|
324
|
-
|
325
|
-
context "block expectations" do
|
326
|
-
setup { 2 }
|
327
|
-
asserts_topic.equals { 1 + 1 }
|
328
|
-
end
|
329
|
-
|
330
|
-
#### More examples/documentation
|
331
|
-
|
332
|
-
There are many more basic assertion modifiers to come. See below for writing Riot extensions if you want to help out.
|
333
|
-
|
334
|
-
See the TODO section for everything that's missing.
|
335
|
-
|
336
|
-
Also, see [the wiki](http://wiki.github.com/thumblemonks/riot) for more examples and documentation.
|
337
|
-
|
338
|
-
<a name="omg"></a>
|
339
|
-
## You say, "OMG! Why did you write this?"
|
340
|
-
|
341
|
-
### Some background, I guess
|
342
|
-
|
343
|
-
You start a new project. You get all excited. You're adding tests. You're adding factories. You're fixturating your setups. You're adding more tests. Your tests start slowing down, but you need to keep pushing because your backlog has a lot of new, nifty features in it. You've got 3000+ lines of test code, 2000+ assertions. Your tests are annoyingly slow. Your tests have become a burden.
|
344
|
-
|
345
|
-
I hate this and it happens a lot, even when I'm conscience that it's happening. Even when I'm not hitting the database and I'm mocking the crap out my code.
|
346
|
-
|
347
|
-
I really, really hate slow test suites.
|
348
|
-
|
349
|
-
#### Did ... you look at Shoulda
|
350
|
-
|
351
|
-
I should say that I love Shoulda in theory and in practice. It changed the way I coded. I added macros all over the place. I built macros into the gems I wrote for the gem itself. But, alas, Shoulda is slow. Shoulda is based on Test::Unit. Shoulda reruns setups for every should. Shoulda could make my life even easier with even more expressiveness.
|
352
|
-
|
353
|
-
#### Did ... you look at RSpec
|
354
|
-
|
355
|
-
:| yes, no, I don't care. It's slow, too. Anyways, I was at [ObjectMentor](http://objectmentor.com) many, many moons ago when Dave Astels (accompanied by David Chelimsky) were explaining this brand new approach to testing called BDD. Mr. Astels explained to us that we if we already understood TDD, then BDD wouldn't help a bunch. Why argue with him?
|
356
|
-
|
357
|
-
### How Riot is the same
|
358
|
-
|
359
|
-
1. It defines a context
|
360
|
-
1. It prints .'s, F's, and E's when tests pass, fail, or error
|
361
|
-
1. It tells you how long it took to run just the tests
|
362
|
-
1. Contexts can be nested and setups inherited
|
363
54
|
|
364
|
-
###
|
55
|
+
### Using assertion macros
|
365
56
|
|
366
|
-
|
57
|
+
Macros greatly simplify a lot of assertions by capturing common practises, e.g. comparing
|
58
|
+
some value using the `==` operator. They work by calling the macro method on the return
|
59
|
+
value of `asserts`:
|
367
60
|
|
368
|
-
|
369
|
-
the test data it is running in. Following this allows me to require
|
370
|
-
setup be run only once for a collection of related assertions. Even in
|
371
|
-
a nested context where setups are inherited, the setup's are called
|
372
|
-
only once per the specific context. Furthermore, teardowns are also
|
373
|
-
called only once per the specific context, after all the setups and
|
374
|
-
assertions have run.
|
61
|
+
asserts("#first") { topic.first }.equals("foo")
|
375
62
|
|
376
|
-
|
63
|
+
Other macros are available; for a complete overview, see {Riot::AssertionMacro}.
|
377
64
|
|
378
|
-
|
65
|
+
A common pattern is to make assertions on the return value of some attribute or method
|
66
|
+
on the `topic` -- to simplify this, we have provided an easy to use shorthand. Simply
|
67
|
+
give a Symbol as the first argument to `asserts` and leave out the block.
|
379
68
|
|
380
|
-
|
69
|
+
asserts(:first).equals("foo")
|
381
70
|
|
382
|
-
I like this approach because I only want to test one thing, but that one thing may require some work on my behalf to get the value. Riot does not let me cheat in this regard. There is no way for me to add more than one assertion to an assertion block.
|
383
71
|
|
384
|
-
|
72
|
+
### Reading Riot's output
|
385
73
|
|
386
|
-
|
74
|
+
Riot can output the test results in several ways, the default being *story reporting*. With
|
75
|
+
this reporter, the output looks like the following:
|
387
76
|
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
task :default => [:test]
|
77
|
+
!!!plain
|
78
|
+
An Array
|
79
|
+
+ asserts is empty
|
80
|
+
An Array with one element
|
81
|
+
+ asserts is not empty
|
82
|
+
+ asserts returns the element on #first
|
395
83
|
|
396
|
-
|
397
|
-
Rake::TestTask.new(:test) do |test|
|
398
|
-
test.libs << 'lib' << 'test'
|
399
|
-
test.pattern = 'test/**/*_test.rb'
|
400
|
-
test.verbose = false
|
401
|
-
end
|
84
|
+
3 passes, 0 failures, 0 errors in 0.000181 seconds
|
402
85
|
|
403
|
-
|
86
|
+
The various shorthands and macros are built in a way that ensures expressive and specific
|
87
|
+
feedback, e.g. the assertion
|
404
88
|
|
405
|
-
|
406
|
-
### With Sinatra
|
89
|
+
asserts(:full_name).matches(/^\w+ \w+$/)
|
407
90
|
|
408
|
-
|
91
|
+
yields the output
|
409
92
|
|
410
|
-
|
411
|
-
|
412
|
-
include Rack::Test::Methods
|
413
|
-
def app; @app; end
|
414
|
-
end
|
93
|
+
!!!plain
|
94
|
+
+ asserts #full_name matches /^\w+ \w+$/
|
415
95
|
|
416
|
-
And then define a context (or many) for testing your Sinatra app. For instance:
|
417
96
|
|
418
|
-
|
419
|
-
|
420
|
-
context 'Some App' do
|
421
|
-
setup { @app = SomeApp }
|
422
|
-
|
423
|
-
context "/index" do
|
424
|
-
setup { get '/' }
|
425
|
-
# ...
|
426
|
-
end
|
427
|
-
end
|
97
|
+
### Testing Rack application
|
428
98
|
|
429
|
-
|
99
|
+
Rack applications can easily be tested using [Riot::Rack](http://github.com/dasch/riot-rack),
|
100
|
+
which integrates [Rack::Test](http://github.com/brynary/rack-test) into Riot.
|
430
101
|
|
431
102
|
require 'riot'
|
432
|
-
require '
|
103
|
+
require 'riot/rack'
|
433
104
|
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
<a name="extending"></a>
|
440
|
-
## Extending Riot with Macros
|
441
|
-
|
442
|
-
To extend Riot, similar to how you would with Shoulda, you simply need to include your methods into the `Riot::Context` class. For example, let's say you wanted to add a helpful macro for asserting the response status of some HTTP result in Sinatra. You could do this easily by defining your macro like so:
|
443
|
-
|
444
|
-
module Custom
|
445
|
-
module Macros
|
446
|
-
def asserts_response_status(expected)
|
447
|
-
asserts("response status is #{expected}") do
|
448
|
-
last_response.status
|
449
|
-
end.equals(expected)
|
450
|
-
end
|
451
|
-
end # Macros
|
452
|
-
end # Custom
|
453
|
-
Riot::Context.instance_eval { include Custom::Macros }
|
454
|
-
|
455
|
-
And then in your actual test, you might do the following:
|
456
|
-
|
457
|
-
context 'Some App' do
|
458
|
-
setup { @app = SomeApp }
|
459
|
-
|
460
|
-
context "/index" do
|
461
|
-
setup { get '/' }
|
462
|
-
asserts_response_status 200
|
463
|
-
end
|
464
|
-
end
|
465
|
-
|
466
|
-
**COMING SOON:** Riot will look into test/riot\_macros, but not today.
|
105
|
+
context "HelloWorldApp" do
|
106
|
+
# Specify your app using the #app helper. If you don't specify
|
107
|
+
# one, Riot::Rack will recursively look for a config.ru file.
|
108
|
+
app { HelloWorldApp }
|
467
109
|
|
468
|
-
|
469
|
-
|
110
|
+
# You can use all the Rack::Test helpers in the setup blocks.
|
111
|
+
setup { get '/' }
|
470
112
|
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
module Foo
|
476
|
-
class KindOfMacro < Riot::AssertionMacro
|
477
|
-
register :kind_of
|
113
|
+
# You can access the response directly.
|
114
|
+
asserts(:status).equals(200)
|
115
|
+
asserts(:body).equals("Hello, World!")
|
116
|
+
end
|
478
117
|
|
479
|
-
def evaluate(actual, expected)
|
480
|
-
if actual.kind_of?(expected)
|
481
|
-
pass("is a kind of #{expected.inspect}")
|
482
|
-
else
|
483
|
-
fail("expected kind of #{expected}, not #{actual.inspect}")
|
484
|
-
end
|
485
|
-
end
|
486
|
-
end # KindOfMacro
|
487
|
-
end # Foo
|
488
118
|
|
489
|
-
|
119
|
+
## Contributing
|
490
120
|
|
491
|
-
|
492
|
-
|
493
|
-
|
121
|
+
Riot is still in its infancy, so both the internal and external API may change radically.
|
122
|
+
That being said, we would love to hear any thoughts and ideas, and bug reports are always
|
123
|
+
welcome. We hang out in `#riot` on `irc.freenode.net`.
|
494
124
|
|
495
|
-
|
125
|
+
The code is hosted on [GitHub](http://github.com), and can be fetched with
|
126
|
+
[Git](http://git-scm.com) by running:
|
496
127
|
|
497
|
-
|
498
|
-
|
499
|
-
register :raises
|
500
|
-
expects_exception! # THE IMPORTANT LINE
|
128
|
+
!!!plain
|
129
|
+
git clone git://github.com/thumblemonks/riot.git
|
501
130
|
|
502
|
-
def evaluate(actual, expected)
|
503
|
-
# ... pass/fail code
|
504
|
-
end
|
505
|
-
end # RaisesMacro
|
506
|
-
end # Foo
|
507
131
|
|
132
|
+
## License
|
508
133
|
|
509
|
-
|
134
|
+
Riot is released under the MIT license. See {file:MIT-LICENSE}.
|