kintama 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|