riot 0.9.12 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/.gitignore +1 -0
  2. data/README.markdown +37 -45
  3. data/VERSION +1 -1
  4. data/lib/riot.rb +27 -18
  5. data/lib/riot/assertion_macros.rb +63 -53
  6. data/lib/riot/context.rb +26 -36
  7. data/lib/riot/reporter.rb +77 -0
  8. data/lib/riot/runnable.rb +50 -0
  9. data/lib/riot/situation.rb +6 -3
  10. data/riot.gemspec +30 -21
  11. data/test/assertion_macros/assigns_test.rb +24 -0
  12. data/test/assertion_macros/equals_test.rb +18 -0
  13. data/test/assertion_macros/exists_test.rb +17 -0
  14. data/test/assertion_macros/kind_of_test.rb +17 -0
  15. data/test/assertion_macros/matching_test.rb +12 -0
  16. data/test/assertion_macros/nil_test.rb +13 -0
  17. data/test/assertion_macros/raises_test.rb +33 -0
  18. data/test/assertion_macros/respond_to_test.rb +12 -0
  19. data/test/assertion_macros/same_elements_test.rb +17 -0
  20. data/test/assertion_test.rb +25 -22
  21. data/test/benchmark/riot_vs_minitest.rb +8 -8
  22. data/test/benchmark/same_elements_vs_set.rb +41 -0
  23. data/test/benchmark/simple_context_and_assertions.rb +8 -8
  24. data/test/context_test.rb +68 -83
  25. data/test/report_test.rb +44 -0
  26. data/test/setup_test.rb +17 -0
  27. data/test/situation_test.rb +16 -0
  28. data/test/teststrap.rb +31 -0
  29. metadata +30 -21
  30. data/lib/riot/assertion.rb +0 -37
  31. data/lib/riot/errors.rb +0 -15
  32. data/lib/riot/report.rb +0 -82
  33. data/test/assertion_macros/assertion_macro_assigns_test.rb +0 -40
  34. data/test/assertion_macros/assertion_macro_equals_test.rb +0 -13
  35. data/test/assertion_macros/assertion_macro_exists_test.rb +0 -17
  36. data/test/assertion_macros/assertion_macro_kind_of_test.rb +0 -13
  37. data/test/assertion_macros/assertion_macro_matching_test.rb +0 -17
  38. data/test/assertion_macros/assertion_macro_nil_test.rb +0 -13
  39. data/test/assertion_macros/assertion_macro_raises_test.rb +0 -43
  40. data/test/assertion_macros/assertion_macro_respond_to_test.rb +0 -16
data/.gitignore CHANGED
@@ -1,2 +1,3 @@
1
1
  *.gem
2
2
  pkg
3
+ .*.swp
@@ -2,6 +2,11 @@
2
2
 
3
3
  An extremely fast, expressive, and context-driven unit-testing framework. Protest the slow test.
4
4
 
5
+ When you're done reading here, take a gander at:
6
+
7
+ * [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.
8
+ * [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.
9
+
5
10
  #### Installation
6
11
 
7
12
  The Riot gem is hosted on gemcutter.org. It used to be hosted on GitHub, but they turned of 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:
@@ -17,32 +22,34 @@ Then, simply install the Riot gem like so:
17
22
  I have done a really simple benchmarking (10,000 runs), but right now, Riot is running about **2 times** faster than Test::unit and thusly Shoulda:
18
23
 
19
24
  Rehearsal ----------------------------------------------
20
- Riot 0.620000 0.010000 0.630000 ( 0.647318)
21
- Test::Unit 1.500000 0.010000 1.510000 ( 1.508144)
22
- Shoulda 1.490000 0.000000 1.490000 ( 1.501468)
23
- ------------------------------------- total: 3.630000sec
25
+ Riot 0.360000 0.000000 0.360000 ( 0.364236)
26
+ Test::Unit 1.250000 0.000000 1.250000 ( 1.258466)
27
+ Shoulda 1.270000 0.010000 1.280000 ( 1.277429)
28
+ ------------------------------------- total: 2.890000sec
24
29
 
25
30
  user system total real
26
- Riot 0.630000 0.010000 0.640000 ( 0.640481)
27
- Test::Unit 1.460000 0.000000 1.460000 ( 1.476454)
28
- Shoulda 1.480000 0.010000 1.490000 ( 1.490633)
31
+ Riot 0.360000 0.000000 0.360000 ( 0.353360)
32
+ Test::Unit 1.260000 0.000000 1.260000 ( 1.263777)
33
+ Shoulda 1.270000 0.000000 1.270000 ( 1.270957)
29
34
 
30
35
  "Is it valid?", you ask. *I* think it is. I ain't no cheater, but I might be delusional.
31
36
 
32
37
  To compare against MiniTest, I had to run the benchmark separately.
33
38
 
34
39
  Rehearsal --------------------------------------------
35
- Riot 0.630000 0.010000 0.640000 ( 0.641285)
36
- MiniTest 0.770000 0.070000 0.840000 ( 0.846447)
37
- ----------------------------------- total: 1.480000sec
40
+ Riot 0.350000 0.000000 0.350000 ( 0.354331)
41
+ MiniTest 0.720000 0.070000 0.790000 ( 0.793093)
42
+ ----------------------------------- total: 1.140000sec
38
43
 
39
44
  user system total real
40
- Riot 0.630000 0.000000 0.630000 ( 0.632337)
41
- MiniTest 0.730000 0.070000 0.800000 ( 0.798107)
45
+ Riot 0.350000 0.000000 0.350000 ( 0.348796)
46
+ MiniTest 0.730000 0.060000 0.790000 ( 0.801629)
47
+
48
+ Riot is currently about twice as fast as minitest. Riot is also half the code of MiniTest (`310 loc < 674 loc` :)
42
49
 
43
- Riot is currently only slightly faster, but I haven't done any optimization yet. Riot is also half the code of MiniTest (`313 loc < 674 loc` :)
50
+ All tests ran with `ruby 1.8.7 (2009-06-12 patchlevel 174) [i686-darwin9.8.0], MBARI 0x8770, Ruby Enterprise Edition 2009.10`.
44
51
 
45
- All tests ran with `ruby 1.8.7 (2009-06-12 patchlevel 174) [i686-darwin9]`.
52
+ 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.
46
53
 
47
54
  ## Examples
48
55
 
@@ -50,7 +57,7 @@ All tests ran with `ruby 1.8.7 (2009-06-12 patchlevel 174) [i686-darwin9]`.
50
57
 
51
58
  **NOTE:** For no specific reason, I'm going to use an ActiveRecord model in the following examples.
52
59
 
53
- 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 fact aliases. The only difference is in the way you write the assertion.
60
+ 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.
54
61
 
55
62
  For instance, given a test file named `foo_test.rb`, you might have the following code in it:
56
63
 
@@ -72,7 +79,7 @@ Notice that you do not define a class anywhere. That would be the entire content
72
79
 
73
80
  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.
74
81
 
75
- 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 setup in the given context. In the examples above, `User.new` was returned, and is therefor accessible as the `topic`.
82
+ 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`.
76
83
 
77
84
  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.
78
85
 
@@ -90,7 +97,7 @@ This is awfully redundant - not to mention, contrived. So, we wrote a shortcut t
90
97
 
91
98
  context "a billionaire" do
92
99
  setup { MoneyMaker.build(:billionaire) }
93
- topic.kind_of(Billionaire)
100
+ asserts_topic.kind_of(Billionaire)
94
101
  end
95
102
 
96
103
  #### Example: Equality
@@ -104,7 +111,7 @@ For example:
104
111
  asserts("email address") { topic.email }.equals('foo@bar.com')
105
112
  end
106
113
 
107
- 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 `@user.email`, the assertion would have failed.
114
+ 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.
108
115
 
109
116
  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:
110
117
 
@@ -265,7 +272,7 @@ I should say that I love Shoulda in theory and in practice. It changed the way I
265
272
 
266
273
  ### How Riot is different
267
274
 
268
- Riot differs primarily in that it does not rerun setup for each test in a context. I know this is going to shock and awe a lot of folks. However, over the past several years of my doing TDD in some capacity or another, there are certain habits I have tried to pick up on any many others I have tried to drop.
275
+ Riot differs primarily in that it does not rerun setup for each test in a context. I know this is going to shock a lot of folks. However, over the past several years of my doing TDD in some capacity or another, there are certain habits I have tried to pick up on any many others I have tried to drop.
269
276
 
270
277
  For instance, I believe that no assertion should mangle the context of the test data it is running in. Following this allows me to require setup be run only once for a collection of related assertions. Even in a nested context where setups are inherited, the setup's are called only once per the specific context.
271
278
 
@@ -302,7 +309,7 @@ Basically, it's like setting up any other test runner. Then, from the command li
302
309
  Riot definitely works with the latest Sinatra. I personally use it to run tests for [Chicago](http://github.com/thumblemonks/chicago) and [Slvu](http://github.com/jaknowlden/slvu). Setup is pretty easy and very much like getting your tests to run with Test::Unit. In a test helper file that gets loaded into all of your tests (that need it), enter the following:
303
310
 
304
311
  require 'riot'
305
- class Riot::Context
312
+ class Riot::Situation
306
313
  include Rack::Test::Methods
307
314
  def app; @app; end
308
315
  end
@@ -356,18 +363,19 @@ And then in your actual test, you might do the following:
356
363
 
357
364
  #### Assertion macros
358
365
 
359
- If you want to add special macros to an Assertion, this is as easy as adding them to a Context. Similar to Context macros, Assertion macros are included into the Assertion class.
366
+ If you want to add special macros to an Assertion, this is as easy as adding them to a Context. Assertion macros, however, have a special mechanism for adding themselves onto an assertion. Thus, you will want to open the Riot::Assertion class and then define your assertion macro.
360
367
 
361
368
  For instance, let's say you wanted to add a macro for verifying that the result of an assertion is the same kind\_of object as you would expect. You would define the macro like so:
362
369
 
363
- module Custom
364
- module AssertionMacros
365
- def kind_of(expected_class)
366
- actual.kind_of?(expected) || fail("expected kind of #{expected}, not #{actual.inspect}")
370
+ module Riot
371
+ class Assertion
372
+
373
+ assertion(:kind_of) do |actual, expected|
374
+ actual.kind_of?(expected) ? pass : fail("expected kind of #{expected}, not #{actual.inspect}")
367
375
  end
368
- end # AssertionMacros
369
- end # Custom
370
- Riot::Assertion.instance_eval { include Custom::AssertionMacros }
376
+
377
+ end # Assertion
378
+ end # Riot
371
379
 
372
380
  And in your context, you would use it like so:
373
381
 
@@ -375,20 +383,4 @@ And in your context, you would use it like so:
375
383
  asserts("a hash is defined") { {:foo => 'bar'} }.kind_of(Hash)
376
384
  end
377
385
 
378
- Notice in the new macro we defined the use of the magical **actual** variable. `actual` is evaluated when the assertion is defined and made available to any Assertion macro. If you think you might want to chain assertions checks together, know that only the last failure will get recorded.
379
-
380
- ## TODO
381
-
382
- TONS OF STUFF
383
-
384
- 1. Documentation about the `topic` method
385
- 1. Better documentation for everything
386
- 1. Refactor reporting; some abstracting is needed for recording a result (for instance)
387
- 1. Need to know where in backtrace a test failed (line number, etc.); i.e. backtrace filtering for clarity
388
- 1. More assertion macros: throws, etc.
389
- 1. Aliases for context "with, without, when, ..."; add those words to test description
390
- 1. Optimization and simplification (ex. flog is complaining print\_result\_stack)
391
- 1. Better error messages?
392
- 1. Perhaps: Multiple setup blocks in one context
393
- 1. Perhaps: association macro chaining
394
- 1. Perhaps: Uhhhhh ... a teardown method (only maybe. not sold :)
386
+ If you think you might want to chain assertions checks together, this won't work out the box. The reason for this is that your assertion macro should call out to pass or fail, which each return a signal that will be used by the reporter. You could conceivably write an assertion as a filter macro instead, that would return `self`, but I can't decide why you would do that yet (in theory it would work, though).
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.9.12
1
+ 0.10.0
@@ -1,28 +1,37 @@
1
- require 'riot/errors'
2
- require 'riot/report'
3
- require 'riot/situation'
1
+ require 'riot/reporter'
4
2
  require 'riot/context'
5
- require 'riot/assertion'
3
+ require 'riot/situation'
4
+ require 'riot/runnable'
6
5
  require 'riot/assertion_macros'
7
6
 
8
7
  module Riot
8
+ def self.context(description, &definition)
9
+ root_contexts << Context.new(description, &definition)
10
+ end
9
11
 
10
- # Configuration
12
+ def self.root_contexts; @root_contexts ||= []; end
11
13
 
12
- def self.reporter; @reporter ||= (Riot.silently? ? NilReport.new : TextReport.new); end
13
- def self.reporter=(report); @reporter = report; end
14
- def self.silently!; @silently = true; end
15
- def self.silently?; @silently || false; end
14
+ def self.run
15
+ the_reporter = reporter.new
16
+ the_reporter.summarize do
17
+ root_contexts.each { |ctx| ctx.run(the_reporter) }
18
+ end
19
+ end
20
+
21
+ def self.silently!; @silent = true; end
22
+ def self.silently?; @silent == true; end
23
+
24
+ def self.reporter
25
+ Riot.silently? ? Riot::SilentReporter : (@reporter_class || Riot::StoryReporter)
26
+ end
27
+ def self.reporter=(reporter_class) @reporter_class = reporter_class; end
28
+ def self.dots; Riot.reporter = Riot::DotMatrixReporter; end
16
29
 
17
- at_exit do
18
- Riot.reporter.results
19
- exit false unless reporter.passed?
20
- end unless Riot.silently?
30
+ at_exit { run unless Riot.silently? }
21
31
  end # Riot
22
32
 
23
- module Kernel
24
- def context(description, reporter = nil, parent = nil, &block)
25
- reporter ||= Riot.reporter
26
- reporter.time { Riot::Context.new(description, reporter, parent, &block) }
33
+ class Object
34
+ def context(description, &definition)
35
+ Riot.context(description, &definition)
27
36
  end
28
- end # Kernel
37
+ end
@@ -1,17 +1,17 @@
1
1
  module Riot
2
- module AssertionMacros
2
+ class Assertion
3
3
  # Asserts that the result of the test equals the expected value
4
4
  # asserts("test") { "foo" }.equals("foo")
5
5
  # should("test") { "foo" }.equals("foo")
6
- def equals(expected)
7
- expected == actual || fail("expected #{expected.inspect}, not #{actual.inspect}")
6
+ assertion(:equals) do |actual, expected|
7
+ expected == actual ? pass : fail("expected #{actual.inspect} to equal #{expected.inspect}")
8
8
  end
9
9
 
10
10
  # Asserts that the result of the test is nil
11
11
  # asserts("test") { nil }.nil
12
12
  # should("test") { nil }.nil
13
- def nil
14
- actual.nil? || fail("expected nil, not #{actual.inspect}")
13
+ assertion(:nil) do |actual|
14
+ actual.nil? ? pass : fail("expected nil, not #{actual.inspect}")
15
15
  end
16
16
 
17
17
  # Asserts that the result of the test is a non-nil value. This is useful in the case where you don't want
@@ -20,72 +20,82 @@ module Riot
20
20
  # should("test") { 123 }.exists
21
21
  # asserts("test") { "" }.exists
22
22
  # asserts("test") { nil }.exists # This would fail
23
- def exists
24
- !actual.nil? || fail("expected a non-nil value")
23
+ assertion(:exists) do |actual|
24
+ !actual.nil? ? pass : fail("expected a non-nil value")
25
25
  end
26
26
 
27
- # Asserts that the test raises the expected Exception
28
- # asserts("test") { raise My::Exception }.raises(My::Exception)
29
- # should("test") { raise My::Exception }.raises(My::Exception)
27
+ # Asserts that the result of the test is an object that is a kind of the expected type
28
+ # asserts("test") { "foo" }.kind_of(String)
29
+ # should("test") { "foo" }.kind_of(String)
30
+ assertion(:kind_of) do |actual, expected|
31
+ actual.kind_of?(expected) ? pass : fail("expected kind of #{expected}, not #{actual.inspect}")
32
+ end
33
+
34
+ # Asserts that an instance variable is defined for the result of the assertion. Value of instance
35
+ # variable is expected to not be nil
36
+ # setup { User.new(:email => "foo@bar.baz") }
37
+ # topic.assigns(:email)
30
38
  #
31
- # You can also check to see if the provided message equals or matches your expectations. The message from
32
- # the actual raised exception will be converted to a string before any comparison is executed.
33
- # asserts("test") { raise My::Exception, "Foo" }.raises(My::Exception, "Foo")
34
- # asserts("test") { raise My::Exception, "Foo Bar" }.raises(My::Exception, /Bar/)
35
- # asserts("test") { raise My::Exception, ["a", "b"] }.raises(My::Exception, "ab")
36
- def raises(expected, expected_message=nil)
37
- actual_error = raised && raised.original_exception
38
- @raised = nil
39
- if actual_error.nil?
40
- return fail("should have raised #{expected}, but raised nothing")
41
- elsif expected != actual_error.class
42
- return fail("should have raised #{expected}, not #{actual_error.class}")
43
- elsif expected_message && !(actual_error.message.to_s =~ %r[#{expected_message}])
44
- return fail("expected #{expected_message} for message, not #{actual_error.message}")
39
+ # If a value is provided in addition to the variable name, the actual value of the instance variable
40
+ # must equal the expected value
41
+ # setup { User.new(:email => "foo@bar.baz") }
42
+ # topic.assigns(:email, "foo@bar.baz")
43
+ assertion(:assigns) do |actual, *expectings|
44
+ variable, expected_value = expectings
45
+ actual_value = actual.instance_variable_get("@#{variable}")
46
+ if actual_value.nil?
47
+ fail("expected @#{variable} to be assigned a value")
48
+ elsif !expected_value.nil? && expected_value != actual_value
49
+ fail(%Q[expected @#{variable} to be equal to #{expected_value.inspect}, not #{actual_value.inspect}])
50
+ else
51
+ pass
45
52
  end
46
- true
47
53
  end
48
54
 
49
55
  # Asserts that the result of the test equals matches against the proved expression
50
56
  # asserts("test") { "12345" }.matches(/\d+/)
51
57
  # should("test") { "12345" }.matches(/\d+/)
52
- def matches(expected)
58
+ assertion(:matches) do |actual, expected|
53
59
  expected = %r[#{Regexp.escape(expected)}] if expected.kind_of?(String)
54
- actual =~ expected || fail("expected #{expected.inspect} to match #{actual.inspect}")
55
- end
56
-
57
- # Asserts that the result of the test is an object that is a kind of the expected type
58
- # asserts("test") { "foo" }.kind_of(String)
59
- # should("test") { "foo" }.kind_of(String)
60
- def kind_of(expected)
61
- actual.kind_of?(expected) || fail("expected kind of #{expected}, not #{actual.inspect}")
60
+ actual =~ expected ? pass : fail("expected #{expected.inspect} to match #{actual.inspect}")
62
61
  end
63
62
 
64
63
  # Asserts that the result of the test is an object that responds to the given method
65
64
  # asserts("test") { "foo" }.respond_to(:to_s)
66
65
  # should("test") { "foo" }.respond_to(:to_s)
67
- def respond_to(expected)
68
- actual.respond_to?(expected) || fail("expected method #{expected.inspect} is not defined")
66
+ assertion(:respond_to) do |actual, expected|
67
+ actual.respond_to?(expected) ? pass : fail("expected method #{expected.inspect} is not defined")
69
68
  end
70
69
 
71
- # Asserts that an instance variable is defined for the result of the assertion. Value of instance
72
- # variable is expected to not be nil
73
- # setup { User.new(:email => "foo@bar.baz") }
74
- # topic.assigns(:email)
70
+ # Asserts that two arrays contain the same elements, the same number of times.
71
+ # asserts("test") { ["foo", "bar"] }.same_elements(["bar", "foo"])
72
+ # should("test") { ["foo", "bar"] }.same_elements(["bar", "foo"])
73
+ assertion(:same_elements) do |actual, expected|
74
+ require 'set'
75
+ same = (Set.new(expected) == Set.new(actual))
76
+ same ? pass : fail("expected elements #{expected.inspect} to match #{actual.inspect}")
77
+ end
78
+
79
+ # Asserts that the test raises the expected Exception
80
+ # asserts("test") { raise My::Exception }.raises(My::Exception)
81
+ # should("test") { raise My::Exception }.raises(My::Exception)
75
82
  #
76
- # If a value is provided in addition to the variable name, the actual value of the instance variable
77
- # must equal the expected value
78
- # setup { User.new(:email => "foo@bar.baz") }
79
- # topic.assigns(:email, "foo@bar.baz")
80
- def assigns(variable, expected_value=nil)
81
- actual_value = actual.instance_variable_get("@#{variable}")
82
- return fail("expected @#{variable} to be assigned a value") if actual_value.nil?
83
- unless expected_value.nil? || expected_value == actual_value
84
- return fail(%Q[expected @#{variable} to be equal to '#{expected_value}', not '#{actual_value}'])
83
+ # You can also check to see if the provided message equals or matches your expectations. The message
84
+ # from the actual raised exception will be converted to a string before any comparison is executed.
85
+ # asserts("test") { raise My::Exception, "Foo" }.raises(My::Exception, "Foo")
86
+ # asserts("test") { raise My::Exception, "Foo Bar" }.raises(My::Exception, /Bar/)
87
+ assertion(:raises, expect_exception=true) do |actual_exception, expected_class, expected_message|
88
+ actual_message = actual_exception && actual_exception.message
89
+ if actual_exception.nil?
90
+ fail("should have raised #{expected_class}, but raised nothing")
91
+ elsif expected_class != actual_exception.class
92
+ fail("should have raised #{expected_class}, not #{actual_exception.class}")
93
+ elsif expected_message && !(actual_message.to_s =~ %r[#{expected_message}])
94
+ fail("expected #{expected_message.inspect} for message, not #{actual_message.inspect}")
95
+ else
96
+ pass
85
97
  end
86
- true
87
98
  end
88
- end # AssertionMacros
89
- end # Riot
90
99
 
91
- Riot::Assertion.instance_eval { include Riot::AssertionMacros }
100
+ end # Assertion
101
+ end # Riot
@@ -1,48 +1,38 @@
1
1
  module Riot
2
2
  class Context
3
- # The protein
4
- attr_reader :description, :assertions, :situation
5
- def initialize(description, reporter, parent=nil, &context_block)
6
- @description, @reporter, @parent = description, reporter, parent
7
- @setups, @assertions = [], []
8
- @situation = Situation.new
9
- bootstrap(@situation)
10
- instance_eval(&context_block) if block_given? # running the context
11
- report
3
+ def initialize(description, setups=[], &definition)
4
+ @description = description
5
+ @contexts, @setups, @assertions = [], setups, []
6
+ self.instance_eval(&definition)
12
7
  end
13
-
14
- # Walk it out. Call setup from parent first
15
- def bootstrap(a_situation)
16
- @parent.bootstrap(a_situation).each do |setup_block|
17
- run_setup(a_situation, &setup_block)
18
- end if @parent
19
- @setups
20
- end
21
-
22
- def report
23
- assertions.each { |assertion| @reporter.process_assertion(assertion) }
8
+
9
+ def setup(&definition)
10
+ @setups << Setup.new(&definition)
24
11
  end
25
12
 
26
- def to_s; @to_s ||= [@parent.to_s, @description].join(' ').strip; end
13
+ def asserts(what, &definition) new_assertion("asserts", what, &definition); end
14
+ def should(what, &definition) new_assertion("should", what, &definition); end
27
15
 
28
- # DSLee stuff
16
+ def asserts_topic; asserts("topic") { topic }; end
29
17
 
30
- def setup(&block)
31
- @setups << block
32
- run_setup(situation, &block)
18
+ def context(description, &definition)
19
+ # not liking the dup
20
+ @contexts << Context.new("#{@description} #{description}", @setups.dup, &definition)
33
21
  end
34
-
35
- def context(description, &block) Context.new(description, @reporter, self, &block); end
36
- def asserts(what, &block) add_assertion("asserts #{what}", &block); end
37
- def should(what, &block) add_assertion("should #{what}", &block); end
38
- def topic; asserts("topic") { topic }; end
39
- private
40
- def add_assertion(what, &block)
41
- (assertions << Assertion.new("#{to_s} #{what}", @situation, &block)).last
22
+
23
+ def run(reporter)
24
+ runnables = @setups + @assertions
25
+ reporter.describe_context(@description) unless @assertions.empty?
26
+ situation = Situation.new
27
+ runnables.each do |runnable|
28
+ reporter.report(runnable.to_s, runnable.run(situation))
29
+ end
30
+ @contexts.each { |ctx| ctx.run(reporter) }
31
+ reporter
42
32
  end
43
-
44
- def run_setup(a_situation, &setup_block)
45
- a_situation.topic = a_situation.instance_eval(&setup_block) if setup_block
33
+ private
34
+ def new_assertion(scope, what, &definition)
35
+ (@assertions << Assertion.new("#{scope} #{what}", &definition)).last
46
36
  end
47
37
  end # Context
48
38
  end # Riot