kintama 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +247 -0
- data/lib/kintama.rb +100 -0
- data/lib/kintama/assertions.rb +38 -0
- data/lib/kintama/context.rb +187 -0
- data/lib/kintama/runnable.rb +33 -0
- data/lib/kintama/runner.rb +153 -0
- data/lib/kintama/test.rb +68 -0
- data/test/aliases_test.rb +26 -0
- data/test/assertions_test.rb +42 -0
- data/test/automatic_running_test.rb +45 -0
- data/test/exceptions_test.rb +40 -0
- data/test/kintama_test.rb +114 -0
- data/test/matcher_test.rb +65 -0
- data/test/method_behaviour_test.rb +176 -0
- data/test/pending_test.rb +13 -0
- data/test/runners/base_runner_test.rb +153 -0
- data/test/runners/inline_runner_test.rb +64 -0
- data/test/runners/verbose_runner_test.rb +129 -0
- data/test/setup_test.rb +107 -0
- data/test/teardown_test.rb +92 -0
- data/test/test_and_subcontext_access_test.rb +110 -0
- data/test/test_helper.rb +36 -0
- metadata +89 -0
data/README.md
ADDED
@@ -0,0 +1,247 @@
|
|
1
|
+
Hello
|
2
|
+
=====
|
3
|
+
|
4
|
+
This is a tool for testing code. Or maybe it's a tool for exploring ways to test code. See below.
|
5
|
+
|
6
|
+
Huh? Really? Another one?
|
7
|
+
====
|
8
|
+
|
9
|
+
... Yeah, I know. To be honest, I'm not 100% sure why I'm doing this. Here are some guesses though:
|
10
|
+
|
11
|
+
My testing tools of choice, at the moment, are [Test::Unit][] with [shoulda][] to provide nested contexts, but not really it's macros.
|
12
|
+
|
13
|
+
I'm not a huge fan of [Test::Unit][]. Whenever I've tried to extend its behaviour I've hit snags, and found its code difficult to understand (particularly as lots of it don't seem to be regularly used - I'm looking at you, [TkRunner][] and friends). I also don't really love [RSpec][], but I think that's just a personal preference (I learned with test/unit, and I didn't want to relearn all of the matcher stuff).
|
14
|
+
|
15
|
+
I like [shoulda][], because like [RSpec][], it lets me nest groups of tests in ways that help remove duplication in setups. However, [I don't have a lot of confidence that shoulda is going to stick around in its current, useful-for-stuff-that-isnt-RSpec form](http://robots.thoughtbot.com/post/701863189/shoulda-rails3-and-beyond).
|
16
|
+
|
17
|
+
I like some of the more verbose output that [Cucumber][] and [RSpec][] produce, but as I mentioned above, I don't care for [RSpec][]'s matcher-heavy syntax. It's basically impossible to reproduce that output on anything that uses [Test::Unit][] as a base (see [MonkeySpecDoc][] for an example, which fails because it cannot support any more than one level of nesting)
|
18
|
+
|
19
|
+
I also like things like [`before(:all)`][before_all], and [`fast_context`][fast_context], but don't like having to hack around inside [Test::Unit][] to implement them (I already have with [`test_startup`][test_startup]; it works but who knows for how long).
|
20
|
+
|
21
|
+
|
22
|
+
Related work
|
23
|
+
------------
|
24
|
+
|
25
|
+
In the spirit of [shoulda][], a small library called [context][] adds the simple nested context structures to [Test::Unit][], but that's the problem - we can't build anything on top of [Test::Unit][].
|
26
|
+
|
27
|
+
Ditto for [contest][].
|
28
|
+
|
29
|
+
Probably the closest thing I've seen is [baretest][]. If you look around the code, some of the implementation details are quite similar to those that have evolved in this code (context-ish objects with parents). However, in many ways baretest is more complex, and the final API that it provides is quite foreign compared to [shoulda][].
|
30
|
+
|
31
|
+
Another alternative test framework is [riot][], which claims to be fast, but also appears to constrain the way that tests are written by avoiding instance variables in setups, for example.
|
32
|
+
|
33
|
+
[Testy][] is interesting - it looks like its output is YAML!. [Tryouts][] is thinking outside the box, using comment examples.
|
34
|
+
|
35
|
+
[Zebra][] addresses the apparent duplication of the test name and the test body, but does it by introducing an [RSpec][]-esque method on every object. Wild. Also, it's an extension of [Test::Unit][], so that's strike two for me, personally.
|
36
|
+
|
37
|
+
I have no idea what to make of [Shindo][].
|
38
|
+
|
39
|
+
[Exemplor][]... oh my god why am I contributing to this mess.
|
40
|
+
|
41
|
+
Erm.
|
42
|
+
|
43
|
+
Exploring future testing
|
44
|
+
------------------------
|
45
|
+
|
46
|
+
I wanted to explore how easy it would be to reproduce a test framework with a modern, [shoulda][]/RSpec-esque syntax, but that was simple enough to be understandable when anyone needed to change it.
|
47
|
+
|
48
|
+
I also wanted to be able to start exploring different ways of expressing test behaviour, outside of the classic `setup -> test -> teardown` cycle, but didn't feel that I could use test/unit as a basis for this kind of speculative work without entering a world of pain.
|
49
|
+
|
50
|
+
Hence... _this_.
|
51
|
+
|
52
|
+
|
53
|
+
Examples
|
54
|
+
========
|
55
|
+
|
56
|
+
These will all be very familiar to most people who are already users of [shoulda][]:
|
57
|
+
|
58
|
+
require 'kintama'
|
59
|
+
|
60
|
+
context "A thing" do
|
61
|
+
setup do
|
62
|
+
@thing = Thing.new
|
63
|
+
end
|
64
|
+
should "act like a thing" do
|
65
|
+
assert_equal "thingish", @thing.nature
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
Simple, right? Note that we don't need an outer subclass of `Test::Unit::TestCase`; it's nice to lose that noise, but otherwise so far so same-old-same-old. That's kind-of the point. Anyway, here's what you get when you run this:
|
70
|
+
|
71
|
+
A thing
|
72
|
+
should act like a thing: F
|
73
|
+
|
74
|
+
1 tests, 1 failures
|
75
|
+
|
76
|
+
1) A thing should act like a thing:
|
77
|
+
uninitialized constant Thing (at ./examples/simple.rb:6)
|
78
|
+
|
79
|
+
Firstly, it's formatted nicely. There are no cryptic line numbers or `bind` references like [shoulda][]. If you run it from a terminal, you'll get colour output too. That's nice.
|
80
|
+
|
81
|
+
|
82
|
+
Aliases
|
83
|
+
----
|
84
|
+
|
85
|
+
There are a bunch of aliases you can use in various ways. If you don't like:
|
86
|
+
|
87
|
+
context "A thing" do
|
88
|
+
|
89
|
+
you could also write:
|
90
|
+
|
91
|
+
describe Thing do # like RSpec! ...
|
92
|
+
given "a thing" do # ...
|
93
|
+
testcase "a thing" do # ...
|
94
|
+
|
95
|
+
It's trivial to define other aliases that might make your tests more readable. Similarly for defining the tests themselves, instead of:
|
96
|
+
|
97
|
+
should "act like a thing" do
|
98
|
+
|
99
|
+
you might prefer:
|
100
|
+
|
101
|
+
it "should act like a thing" do # ...
|
102
|
+
test "acts like a thing" do # ...
|
103
|
+
|
104
|
+
Sometimes just having that flexibility makes all the difference.
|
105
|
+
|
106
|
+
|
107
|
+
Setup, teardown, nested contexts
|
108
|
+
--------------
|
109
|
+
|
110
|
+
These work as you'd expect based on shoulda or RSpec:
|
111
|
+
|
112
|
+
given "a Thing" do
|
113
|
+
setup do
|
114
|
+
@thing = Thing.new
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should be happy" do
|
118
|
+
assert @thing.happy?
|
119
|
+
end
|
120
|
+
|
121
|
+
context "that is prodded" do
|
122
|
+
setup do
|
123
|
+
@thing.prod!
|
124
|
+
end
|
125
|
+
|
126
|
+
should "not be happy" do
|
127
|
+
assert_false @thing.happy?
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
teardown do
|
132
|
+
@thing.cleanup_or_something
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
You can also add (several) global `setup` and `teardown` blocks, which will be run before (or after) every test. For example:
|
137
|
+
|
138
|
+
Kintama.setup do
|
139
|
+
@app = ThingApp.new
|
140
|
+
end
|
141
|
+
|
142
|
+
given "a request" do
|
143
|
+
it "should work" do
|
144
|
+
assert_equal 200, @app.response.status
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
Helpers
|
150
|
+
-------
|
151
|
+
|
152
|
+
If you want to make methods available in your tests, you have a few options. You can define them inline:
|
153
|
+
|
154
|
+
context "my face" do
|
155
|
+
should "be awesome" do
|
156
|
+
assert_equal "awesome", create_face.status
|
157
|
+
end
|
158
|
+
|
159
|
+
helpers do
|
160
|
+
def create_face
|
161
|
+
Face.new(:name => "james", :eyes => "blue", :something => "something else")
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
Ideally I would've liked to make this syntatically similar to defining a private method in a class, but for various reasons that was not possible. Anyway, your other options are including a module:
|
167
|
+
|
168
|
+
module FaceHelper
|
169
|
+
def create_face
|
170
|
+
# etc ...
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
context "my face" do
|
175
|
+
include FaceHelper
|
176
|
+
should "be awesome" do
|
177
|
+
assert_equal "awesome", create_face.status
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
Or, if you're going to use the method in all your tests, you can add the module globally:
|
182
|
+
|
183
|
+
Kintama.add FaceHelper
|
184
|
+
|
185
|
+
or just define the method globally:
|
186
|
+
|
187
|
+
Kintama.add do
|
188
|
+
def create_face
|
189
|
+
# etc ...
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
### Aside: what happens if you do define a method in the context?
|
194
|
+
|
195
|
+
It becomes available within context (and subcontext) definitions. Here's an example:
|
196
|
+
|
197
|
+
context "blah" do
|
198
|
+
def generate_tests_for(thing)
|
199
|
+
it "should work with #{thing}" do
|
200
|
+
assert thing.works
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
[Monkey.new, Tiger.new].each do |t|
|
205
|
+
generate_tests_for(t)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
This is a bit like defining a 'class method' in a `TestCase` and then being able to call it to generate contexts or tests within that `TestCase`. It's not that tricky once you get used to it.
|
210
|
+
|
211
|
+
|
212
|
+
And now, the more experimental stuff
|
213
|
+
====================================
|
214
|
+
|
215
|
+
Wouldn't it be nice to be able to introspect a failed test without having to re-run it? Well, you can. Lets imagine this test:
|
216
|
+
|
217
|
+
context "A thing" do
|
218
|
+
setup do
|
219
|
+
@thing = Thing.new
|
220
|
+
end
|
221
|
+
should "act like a thing" do
|
222
|
+
assert_equal "thingish", @thing.nature
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
Well... TO BE CONTINUED.
|
227
|
+
|
228
|
+
|
229
|
+
|
230
|
+
[Test::Unit]: http://ruby-doc.org/stdlib/libdoc/test/unit/rdoc/
|
231
|
+
[TkRunner]: http://ruby-doc.org/stdlib/libdoc/test/unit/rdoc/classes/Test/Unit/UI/Tk/TestRunner.html
|
232
|
+
[RSpec]: http://rspec.info
|
233
|
+
[Cucumber]: http://cukes.info
|
234
|
+
[MonkeySpecDoc]: http://jgre.org/2008/09/03/monkeyspecdoc/
|
235
|
+
[before_all]: http://rspec.info/documentation/
|
236
|
+
[fast_context]: https://github.com/lifo/fast_context
|
237
|
+
[test_startup]: https://github.com/freerange/test_startup
|
238
|
+
[shoulda]: https://github.com/thoughtbot/shoulda
|
239
|
+
[baretest]: https://github.com/apeiros/baretest
|
240
|
+
[riot]: https://github.com/thumblemonks/riot
|
241
|
+
[context]: https://github.com/jm/context
|
242
|
+
[contest]: https://github.com/citrusbyte/contest
|
243
|
+
[Testy]: https://github.com/ahoward/testy
|
244
|
+
[Tryouts]: https://github.com/delano/tryouts
|
245
|
+
[Zebra]: https://github.com/jamesgolick/zebra
|
246
|
+
[Shindo]: https://github.com/geemus/shindo
|
247
|
+
[Exemplor]: https://github.com/quackingduck/exemplor
|
data/lib/kintama.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
module Kintama
|
2
|
+
autoload :Runnable, 'kintama/runnable'
|
3
|
+
autoload :Context, 'kintama/context'
|
4
|
+
autoload :Test, 'kintama/test'
|
5
|
+
autoload :TestFailure, 'kintama/test'
|
6
|
+
autoload :Runner, 'kintama/runner'
|
7
|
+
autoload :Assertions, 'kintama/assertions'
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def reset
|
11
|
+
@default_context = Class.new(Runnable)
|
12
|
+
@default_context.send(:include, Kintama::Context)
|
13
|
+
end
|
14
|
+
|
15
|
+
def default_context
|
16
|
+
reset unless @default_context
|
17
|
+
@default_context
|
18
|
+
end
|
19
|
+
|
20
|
+
# Create a new context. Aliases are 'testcase' and 'describe'
|
21
|
+
def context(name, parent=default_context, &block)
|
22
|
+
default_context.context(name, parent, &block)
|
23
|
+
end
|
24
|
+
alias_method :testcase, :context
|
25
|
+
alias_method :describe, :context
|
26
|
+
|
27
|
+
# Create a new context starting with "given "
|
28
|
+
def given(name, parent=default_context, &block)
|
29
|
+
default_context.given(name, parent, &block)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Add a setup which will run at the start of every test.
|
33
|
+
def setup(&block)
|
34
|
+
default_context.setup(&block)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Add a teardown which will be run at the end of every test.
|
38
|
+
def teardown(&block)
|
39
|
+
default_context.teardown(&block)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Makes behaviour available within tests:
|
43
|
+
#
|
44
|
+
# module SomeModule
|
45
|
+
# def blah
|
46
|
+
# end
|
47
|
+
# end
|
48
|
+
# Kintama.include SomeModule
|
49
|
+
#
|
50
|
+
# Any methods will then be available within setup, teardown or tests.
|
51
|
+
def include(mod)
|
52
|
+
default_context.send(:include, mod)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Make new testing behaviour available for the definition of tests.
|
56
|
+
# Methods included in this way are available during the definition of tests.
|
57
|
+
def extend(mod)
|
58
|
+
default_context.extend(mod)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Adds the hook to automatically run all known tests using #run when
|
62
|
+
# ruby exits; this is most useful when running a test file from the command
|
63
|
+
# line or from within an editor
|
64
|
+
def add_exit_hook
|
65
|
+
return if @__added_exit_hook
|
66
|
+
at_exit { exit(Runner.default.run ? 0 : 1) }
|
67
|
+
@__added_exit_hook = true
|
68
|
+
end
|
69
|
+
|
70
|
+
# Tries to determine whether or not this is a sensible situation to automatically
|
71
|
+
# run all tests when ruby exits. At the moment, this is true when either:
|
72
|
+
# - the test was run via rake
|
73
|
+
# - the test file was run as the top-level ruby script
|
74
|
+
#
|
75
|
+
# This method will always return false if the environment variable
|
76
|
+
# KINTAMA_EXPLICITLY_DONT_RUN is not nil.
|
77
|
+
def should_run_on_exit?
|
78
|
+
return false if ENV["KINTAMA_EXPLICITLY_DONT_RUN"]
|
79
|
+
return test_file_was_run? || run_via_rake?
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def test_file_was_run?
|
85
|
+
caller.last.split(":").first == $0
|
86
|
+
end
|
87
|
+
|
88
|
+
def run_via_rake?
|
89
|
+
caller.find { |line| File.basename(line.split(":").first) == "rake_test_loader.rb" } != nil
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
[:context, :given, :describe, :testcase].each do |method|
|
95
|
+
unless self.respond_to?(method)
|
96
|
+
eval %|def #{method}(*args, &block); Kintama.#{method}(*args, &block); end|
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
Kintama.add_exit_hook if Kintama.should_run_on_exit?
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Kintama
|
2
|
+
module Assertions
|
3
|
+
def assert(expression, message="failed")
|
4
|
+
raise Kintama::TestFailure, message unless expression
|
5
|
+
end
|
6
|
+
|
7
|
+
def flunk
|
8
|
+
assert false, "flunked."
|
9
|
+
end
|
10
|
+
|
11
|
+
def assert_equal(expected, actual, message="Expected #{expected.inspect} but got #{actual.inspect}")
|
12
|
+
assert actual == expected, message
|
13
|
+
end
|
14
|
+
|
15
|
+
def assert_not_equal(expected, actual, message)
|
16
|
+
assert actual != expected, message
|
17
|
+
end
|
18
|
+
|
19
|
+
def assert_nil(object, message="#{object.inspect} was not nil")
|
20
|
+
assert_equal nil, object, message
|
21
|
+
end
|
22
|
+
|
23
|
+
def assert_not_nil(object, message="should not be nil")
|
24
|
+
assert_not_equal nil, object, message
|
25
|
+
end
|
26
|
+
|
27
|
+
def assert_kind_of(klass, thing, message="should be a kind of #{klass}")
|
28
|
+
assert thing.is_a?(klass)
|
29
|
+
end
|
30
|
+
|
31
|
+
def assert_raises(message="should raise an exception", &block)
|
32
|
+
yield
|
33
|
+
raise Kintama::TestFailure, message
|
34
|
+
rescue
|
35
|
+
# do nothing, we expected this, but now no TestFailure was raised.
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,187 @@
|
|
1
|
+
module Kintama
|
2
|
+
module Context
|
3
|
+
def setup # noop
|
4
|
+
end
|
5
|
+
|
6
|
+
def teardown # noop
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.included(base)
|
10
|
+
base.extend(ClassMethods)
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
|
15
|
+
# Create a new context. If this is called within a context, a new subcontext
|
16
|
+
# will be created. Aliases are 'testcase' and 'describe'
|
17
|
+
def context(name, parent=self, &block)
|
18
|
+
c = Class.new(parent)
|
19
|
+
c.send(:include, Kintama::Context)
|
20
|
+
c.name = name.to_s
|
21
|
+
c.class_eval(&block)
|
22
|
+
c
|
23
|
+
end
|
24
|
+
alias_method :testcase, :context
|
25
|
+
alias_method :describe, :context
|
26
|
+
|
27
|
+
# Create a new context starting with "given "
|
28
|
+
def given(name, parent=self, &block)
|
29
|
+
context("given " + name, parent, &block)
|
30
|
+
end
|
31
|
+
|
32
|
+
def setup_blocks
|
33
|
+
@setup_blocks ||= []
|
34
|
+
end
|
35
|
+
|
36
|
+
def teardown_blocks
|
37
|
+
@teardown_blocks ||= []
|
38
|
+
end
|
39
|
+
|
40
|
+
# Define the setup for this context.
|
41
|
+
# It will also be run for any subcontexts, before their own setup blocks
|
42
|
+
def setup(&block)
|
43
|
+
self.setup_blocks << block
|
44
|
+
|
45
|
+
# redefine setup for the current set of blocks
|
46
|
+
blocks = self.setup_blocks
|
47
|
+
define_method(:setup) do
|
48
|
+
super
|
49
|
+
blocks.each { |b| instance_eval(&b) }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Define the teardown for this context.
|
54
|
+
# It will also be run for any subcontexts, after their own teardown blocks
|
55
|
+
def teardown(&block)
|
56
|
+
self.teardown_blocks << block
|
57
|
+
|
58
|
+
# redefine teardown for the current set of blocks
|
59
|
+
blocks = self.teardown_blocks
|
60
|
+
define_method(:teardown) do
|
61
|
+
blocks.each { |b| instance_eval(&b) }
|
62
|
+
super
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Defines the subject of any matcher-based tests.
|
67
|
+
def subject(&block)
|
68
|
+
define_method(:subject, &block)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Define a test to run in this context.
|
72
|
+
def test(name, &block)
|
73
|
+
c = Class.new(self)
|
74
|
+
c.send(:include, Kintama::Test)
|
75
|
+
c.name = name
|
76
|
+
c.block = block if block_given?
|
77
|
+
end
|
78
|
+
|
79
|
+
# Define a test to run in this context. The test name will start with "should "
|
80
|
+
# You can either supply a name and block, or a matcher. In the latter case, a test
|
81
|
+
# will be generated using that matcher.
|
82
|
+
def should(name_or_matcher, &block)
|
83
|
+
if name_or_matcher.respond_to?(:matches?)
|
84
|
+
test("should " + name_or_matcher.description) do
|
85
|
+
assert name_or_matcher.matches?(subject), name_or_matcher.failure_message
|
86
|
+
end
|
87
|
+
else
|
88
|
+
test("should " + name_or_matcher, &block)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Define a test using a negated matcher, e.g.
|
93
|
+
#
|
94
|
+
# subject { 'a' }
|
95
|
+
# should_not equal('b')
|
96
|
+
#
|
97
|
+
def should_not(matcher)
|
98
|
+
test("should not " + matcher.description) do
|
99
|
+
assert !matcher.matches?(subject), matcher.negative_failure_message
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Define a test to run in this context. The test name will start with "it "
|
104
|
+
def it(name, &block)
|
105
|
+
test("it " + name, &block)
|
106
|
+
end
|
107
|
+
|
108
|
+
def inherited(child)
|
109
|
+
children << child
|
110
|
+
end
|
111
|
+
|
112
|
+
def children
|
113
|
+
@children ||= []
|
114
|
+
end
|
115
|
+
|
116
|
+
def tests
|
117
|
+
children.select { |c| c.is_a_test? }.sort_by { |t| t.name }
|
118
|
+
end
|
119
|
+
|
120
|
+
def subcontexts
|
121
|
+
children.select { |c| c.is_a_context? }.sort_by { |s| s.name }
|
122
|
+
end
|
123
|
+
|
124
|
+
# Returns true if this context has no known failed tests.
|
125
|
+
def passed?
|
126
|
+
failures.empty?
|
127
|
+
end
|
128
|
+
|
129
|
+
# Returns an array of tests in this and all subcontexts which failed in
|
130
|
+
# the previous run
|
131
|
+
def failures
|
132
|
+
ran_tests.select { |t| !t.passed? } + subcontexts.map { |s| s.failures }.flatten
|
133
|
+
end
|
134
|
+
|
135
|
+
def pending
|
136
|
+
tests.select { |t| t.pending? } + subcontexts.map { |s| s.pending }.flatten
|
137
|
+
end
|
138
|
+
|
139
|
+
def [](name)
|
140
|
+
subcontexts.find { |s| s.name == name } || tests.find { |t| t.name == name }
|
141
|
+
end
|
142
|
+
|
143
|
+
def method_missing(name, *args, &block)
|
144
|
+
if self[de_methodize(name)]
|
145
|
+
self[de_methodize(name)]
|
146
|
+
else
|
147
|
+
begin
|
148
|
+
super
|
149
|
+
rescue NameError, NoMethodError => e
|
150
|
+
if parent
|
151
|
+
parent.send(name, *args, &block)
|
152
|
+
else
|
153
|
+
raise e
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def respond_to?(name)
|
160
|
+
self[name] ||
|
161
|
+
super ||
|
162
|
+
(parent ? parent.respond_to?(name) : false)
|
163
|
+
end
|
164
|
+
|
165
|
+
# Runs all tests in this context and any subcontexts.
|
166
|
+
# Returns true if all tests passed; otherwise false
|
167
|
+
def run(runner=nil)
|
168
|
+
@ran_tests = []
|
169
|
+
runner.context_started(self) if runner
|
170
|
+
tests.each { |t| instance = t.new; instance.run(runner); ran_tests << instance }
|
171
|
+
subcontexts.each { |s| s.run(runner) }
|
172
|
+
runner.context_finished(self) if runner
|
173
|
+
passed?
|
174
|
+
end
|
175
|
+
|
176
|
+
private
|
177
|
+
|
178
|
+
def de_methodize(name)
|
179
|
+
name.to_s.gsub("_", " ")
|
180
|
+
end
|
181
|
+
|
182
|
+
def ran_tests
|
183
|
+
@ran_tests || []
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|