riot 0.10.9 → 0.10.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/CHANGELOG +46 -0
  2. data/README.markdown +53 -16
  3. data/Rakefile +8 -0
  4. data/VERSION +1 -1
  5. data/lib/riot.rb +1 -1
  6. data/lib/riot/assertion.rb +18 -31
  7. data/lib/riot/assertion_macro.rb +35 -0
  8. data/lib/riot/assertion_macros/any.rb +12 -0
  9. data/lib/riot/assertion_macros/assigns.rb +27 -0
  10. data/lib/riot/assertion_macros/empty.rb +13 -0
  11. data/lib/riot/assertion_macros/equals.rb +18 -0
  12. data/lib/riot/assertion_macros/exists.rb +15 -0
  13. data/lib/riot/assertion_macros/includes.rb +17 -0
  14. data/lib/riot/assertion_macros/kind_of.rb +16 -0
  15. data/lib/riot/assertion_macros/matches.rb +17 -0
  16. data/lib/riot/assertion_macros/nil.rb +12 -0
  17. data/lib/riot/assertion_macros/raises.rb +31 -0
  18. data/lib/riot/assertion_macros/respond_to.rb +16 -0
  19. data/lib/riot/assertion_macros/same_elements.rb +14 -0
  20. data/lib/riot/assertion_macros/size.rb +15 -0
  21. data/lib/riot/context.rb +9 -17
  22. data/lib/riot/reporter.rb +25 -21
  23. data/riot.gemspec +17 -3
  24. data/test/assertion_macros/any_test.rb +2 -2
  25. data/test/assertion_macros/equals_test.rb +4 -4
  26. data/test/assertion_macros/exists_test.rb +2 -2
  27. data/test/assertion_macros/includes_test.rb +1 -1
  28. data/test/assertion_macros/kind_of_test.rb +1 -1
  29. data/test/assertion_macros/matching_test.rb +1 -1
  30. data/test/assertion_macros/nil_test.rb +1 -1
  31. data/test/assertion_macros/raises_test.rb +3 -3
  32. data/test/assertion_macros/respond_to_test.rb +1 -1
  33. data/test/assertion_macros/size_test.rb +17 -6
  34. data/test/assertion_test.rb +11 -6
  35. data/test/context_test.rb +2 -54
  36. data/test/report_test.rb +7 -2
  37. data/test/teststrap.rb +12 -3
  38. metadata +17 -3
  39. data/lib/riot/assertion_macros.rb +0 -136
@@ -0,0 +1,46 @@
1
+ *0.10.10*
2
+
3
+ * Passing assertion macros can now return a custom message [dasch, jaknowlden]
4
+
5
+ def evaluate(actual, *expectings)
6
+ 1 == 1 ? pass("1 does equal 1") : fail("1 does not equal 1 in this universe")
7
+ end
8
+
9
+ * Removing Context#extend_assertions and related code [jaknowlden]
10
+
11
+ * Allow the use of symbolic descriptions as shorthands for sending the message to the topic [dasch]
12
+
13
+ setup { "foo" }
14
+ asserts(:upcase).equals("FOO")
15
+
16
+ * Added AssertionMacro and #register for macros [jaknowlden, splattael]
17
+
18
+ module My
19
+ class CustomThingAssertion < Riot::AssertionMacro
20
+ register :custom_thing
21
+ expects_exception!
22
+
23
+ def evaluate(actual, *expectings)
24
+ # ...
25
+ end
26
+ end
27
+
28
+ Riot::Assertion.register_macro :custom_thing, CustomThingAssertion
29
+ end
30
+
31
+ * Replace IOReporter#say with #puts. Also add #print. [splattael]
32
+
33
+ class SomeNewReporter < IOReporter
34
+ def pass
35
+ puts "I PASSED"
36
+ end
37
+
38
+ def fail
39
+ print "F"
40
+ end
41
+ # ...
42
+ end
43
+
44
+ *0.10.9 and before*
45
+
46
+ See [commit log](http://github.com/thumblemonks/riot/commits/master)
@@ -44,7 +44,7 @@ Then, simply install the Riot gem like so:
44
44
  <a name="speed"></a>
45
45
  #### Note on speed
46
46
 
47
- 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:
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
48
 
49
49
  Rehearsal ----------------------------------------------
50
50
  Riot 0.360000 0.000000 0.360000 ( 0.364236)
@@ -112,8 +112,9 @@ The other important thing to note in the examples above is the use of the `topic
112
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
113
 
114
114
  <a name="examples-shortcut"></a>
115
- #### Example: Shortcut - Asserting the topic itself
115
+ #### Example: Shortcuts
116
116
 
117
+ ##### Asserting the topic itself
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:
118
119
 
119
120
  context "a billionaire" do
@@ -129,16 +130,31 @@ This is awfully redundant - not to mention, contrived. So, we wrote a shortcut t
129
130
  asserts_topic.kind_of(Billionaire)
130
131
  end
131
132
 
132
- If you'd like to add a description to your assertion about the topic,
133
- but still would like to avoid the redundancy of an assertion simply
134
- returning the topic from it's block, you can omit the block all
135
- together as follows:
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:
136
134
 
137
135
  context "a trazillionaire" do
138
136
  setup { MoneyMaker.build(:trazillionaire) }
139
137
  asserts("is a trazillionaire").kind_of(Trazillionaire)
140
138
  end
141
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
+
142
158
  <a name="examples-equality"></a>
143
159
  #### Example: Equality
144
160
 
@@ -410,7 +426,10 @@ And then define a context (or many) for testing your Sinatra app. For instance:
410
426
  end
411
427
  end
412
428
 
413
- Make sure to check out the Riot + Sinatra testing macros in Chicago.
429
+ Make sure to check out the Riot + Sinatra testing macros in Chicago. If you do happen to use Chicago, the above `test_helper.rb` setup can be compressed to the following while allowing the same test to work:
430
+
431
+ require 'riot'
432
+ require 'chicago/riot'
414
433
 
415
434
  <a name="rails"></a>
416
435
  ### With Rails
@@ -449,19 +468,23 @@ And then in your actual test, you might do the following:
449
468
  <a name="assertion-macros"></a>
450
469
  #### Assertion macros
451
470
 
452
- 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.
471
+ If you want to add special macros to an Assertion, the process is really very easy. You simple need to define a class that extends `AssertionMacro` (or at least quacks like it), be sure to implement the `evaluate` instance method, and then register the macro.
453
472
 
454
- 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:
473
+ 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:
455
474
 
456
- module Riot
457
- class Assertion
475
+ module Foo
476
+ class KindOfMacro < Riot::AssertionMacro
477
+ register :kind_of
458
478
 
459
- assertion(:kind_of) do |actual, expected|
460
- actual.kind_of?(expected) ? pass : fail("expected kind of #{expected}, not #{actual.inspect}")
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
461
485
  end
462
-
463
- end # Assertion
464
- end # Riot
486
+ end # KindOfMacro
487
+ end # Foo
465
488
 
466
489
  And in your context, you would use it like so:
467
490
 
@@ -469,4 +492,18 @@ And in your context, you would use it like so:
469
492
  asserts("a hash is defined") { {:foo => 'bar'} }.kind_of(Hash)
470
493
  end
471
494
 
495
+ If your assertion macro expects to be called only when an exception occurs, there's support for that, too. Check this:
496
+
497
+ module Foo
498
+ class RaisesMacro < Riot::AssertionMacro
499
+ register :raises
500
+ expects_exception! # THE IMPORTANT LINE
501
+
502
+ def evaluate(actual, expected)
503
+ # ... pass/fail code
504
+ end
505
+ end # RaisesMacro
506
+ end # Foo
507
+
508
+
472
509
  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/Rakefile CHANGED
@@ -11,6 +11,14 @@ Rake::TestTask.new(:test) do |test|
11
11
  test.verbose = false
12
12
  end
13
13
 
14
+ desc "Run all of them fancy benchmarks, Howard!"
15
+ task :benchmarks do
16
+ Dir["test/benchmark/*.rb"].each do |file|
17
+ puts ">> Running #{file}"
18
+ puts %x[ruby #{file}]
19
+ end
20
+ end
21
+
14
22
  desc "Run Flog against library (except tests)"
15
23
  task :flog do
16
24
  puts %x[find ./lib -name *.rb | xargs flog]
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.10.9
1
+ 0.10.10
@@ -3,7 +3,7 @@ require 'riot/context'
3
3
  require 'riot/situation'
4
4
  require 'riot/runnable'
5
5
  require 'riot/assertion'
6
- require 'riot/assertion_macros'
6
+ require 'riot/assertion_macro'
7
7
 
8
8
  module Riot
9
9
  def self.context(description, context_class = Context, &definition)
@@ -1,45 +1,32 @@
1
1
  module Riot
2
2
  class Assertion < RunnableBlock
3
-
4
- def initialize(description, &definition)
5
- super
6
- @expectings, @expect_exception, @expectation_block = [], false, nil
7
- @assertion_block = lambda do |actual|
8
- actual ? self.class.pass : self.class.fail("Expected non-false but got #{actual.inspect} instead")
9
- end
10
- end
11
-
12
3
  class << self
13
- def pass() [:pass]; end
14
- def fail(message) [:fail, message]; end
15
- def error(e) [:error, e]; end
16
- def assertions; @assertions ||= {}; end
4
+ def macros; @macros ||= {}; end
17
5
 
18
- # TODO: Make everyone switch to 1.8.7 or above so we can go mostly stateless again.
19
- # Had to do this for 1.8.6 compatibility {:() I'm a sad sock monkey
20
- def assertion(name, expect_exception=false, &assertion_block)
21
- assertions[name] = [expect_exception, assertion_block]
22
- class_eval <<-EOC
23
- def #{name}(*expectings, &expectation_block)
24
- @expectings, @expectation_block = expectings, expectation_block
25
- @expect_exception, @assertion_block = self.class.assertions[#{name.inspect}]
26
- self
27
- end
28
- EOC
6
+ def register_macro(name, assertion_macro, expect_exception=false)
7
+ macros[name.to_s] = assertion_macro.new
29
8
  end
30
9
  end
31
10
 
32
- def run(situation)
33
- @expectings << situation.evaluate(&@expectation_block) if @expectation_block
34
- process_macro(situation, @expect_exception) { |actual| @assertion_block.call(actual, *@expectings) }
11
+ def initialize(description, &definition)
12
+ super
13
+ @expectings, @expectation_block = [], false, nil
14
+ @macro = AssertionMacro.default
35
15
  end
36
16
 
37
- private
38
- def process_macro(situation, expect_exception)
17
+ def run(situation)
18
+ @expectings << situation.evaluate(&@expectation_block) if @expectation_block
39
19
  actual = situation.evaluate(&definition)
40
- yield(expect_exception ? nil : actual)
20
+ @macro.evaluate((@macro.expects_exception? ? nil : actual), *@expectings)
41
21
  rescue Exception => e
42
- expect_exception ? yield(e) : self.class.error(e)
22
+ @macro.expects_exception? ? @macro.evaluate(e, *@expectings) : @macro.error(e)
23
+ end
24
+ private
25
+ def enhance_with_macro(name, *expectings, &expectation_block)
26
+ @macro, @expectings, @expectation_block = self.class.macros[name.to_s], expectings, expectation_block
27
+ raise(NoMethodError, name) unless @macro
28
+ self
43
29
  end
30
+ alias :method_missing :enhance_with_macro
44
31
  end # Assertion
45
32
  end # Riot
@@ -0,0 +1,35 @@
1
+ module Riot
2
+ class AssertionMacro
3
+ class << self
4
+ attr_reader :expects_exception
5
+
6
+ def default; @default_macro ||= new; end
7
+ def expects_exception!; @expects_exception = true; end
8
+ def register(name); Assertion.register_macro name, self; end
9
+ end
10
+
11
+ def pass(message=nil) [:pass, message]; end
12
+ def fail(message) [:fail, message]; end
13
+ def error(e) [:error, e]; end
14
+
15
+ def expects_exception?; self.class.expects_exception; end
16
+
17
+ def evaluate(actual)
18
+ actual ? pass : fail("Expected non-false but got #{actual.inspect} instead")
19
+ end
20
+ end
21
+ end
22
+
23
+ require 'riot/assertion_macros/any'
24
+ require 'riot/assertion_macros/assigns'
25
+ require 'riot/assertion_macros/empty'
26
+ require 'riot/assertion_macros/equals'
27
+ require 'riot/assertion_macros/exists'
28
+ require 'riot/assertion_macros/includes'
29
+ require 'riot/assertion_macros/kind_of'
30
+ require 'riot/assertion_macros/matches'
31
+ require 'riot/assertion_macros/nil'
32
+ require 'riot/assertion_macros/raises'
33
+ require 'riot/assertion_macros/respond_to'
34
+ require 'riot/assertion_macros/same_elements'
35
+ require 'riot/assertion_macros/size'
@@ -0,0 +1,12 @@
1
+ module Riot
2
+ # Asserts the result has items
3
+ # asserts("an array") { [1] }.any
4
+ # asserts("a hash") { {:name => 'washington'} }.any
5
+ class AnyMacro < AssertionMacro
6
+ register :any
7
+
8
+ def evaluate(actual)
9
+ actual.any? ? pass("is not empty") : fail("expected #{actual.inspect} to have items")
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,27 @@
1
+ module Riot
2
+ # Asserts that an instance variable is defined for the result of the assertion. Value of instance
3
+ # variable is expected to not be nil
4
+ # setup { User.new(:email => "foo@bar.baz") }
5
+ # topic.assigns(:email)
6
+ #
7
+ # If a value is provided in addition to the variable name, the actual value of the instance variable
8
+ # must equal the expected value
9
+ # setup { User.new(:email => "foo@bar.baz") }
10
+ # topic.assigns(:email, "foo@bar.baz")
11
+ class AssignsMacro < AssertionMacro
12
+ register :assigns
13
+
14
+ def evaluate(actual, *expectings)
15
+ variable, expected_value = expectings
16
+ variable_name = "@#{variable}"
17
+ actual_value = actual.instance_variable_defined?(variable_name) ? actual.instance_variable_get(variable_name) : nil
18
+ if actual_value.nil?
19
+ fail("expected @#{variable} to be assigned a value")
20
+ elsif !expected_value.nil? && expected_value != actual_value
21
+ fail(%Q[expected @#{variable} to be equal to #{expected_value.inspect}, not #{actual_value.inspect}])
22
+ else
23
+ pass
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,13 @@
1
+ module Riot
2
+ # Asserts the result is empty
3
+ # asserts("a string") { "" }.empty
4
+ # asserts("an array") { [] }.empty
5
+ # asserts("a hash") { Hash.new }.empty
6
+ class EmptyMacro < AssertionMacro
7
+ register :empty
8
+
9
+ def evaluate(actual)
10
+ actual.length == 0 ? pass : fail("expected #{actual.inspect} to be empty")
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ module Riot
2
+ # Asserts that the result of the test equals the expected value. Using the +===+ operator to assert
3
+ # equality.
4
+ # asserts("test") { "foo" }.equals("foo")
5
+ # should("test") { "foo" }.equals("foo")
6
+ # asserts("test") { "foo" }.equals { "foo" }
7
+ class EqualsMacro < AssertionMacro
8
+ register :equals
9
+
10
+ def evaluate(actual, expected)
11
+ if expected === actual
12
+ pass("is equal to #{expected.inspect}")
13
+ else
14
+ fail("expected #{expected.inspect}, not #{actual.inspect}")
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,15 @@
1
+ module Riot
2
+ # Asserts that the result of the test is a non-nil value. This is useful in the case where you don't want
3
+ # to translate the result of the test into a boolean value
4
+ # asserts("test") { "foo" }.exists
5
+ # should("test") { 123 }.exists
6
+ # asserts("test") { "" }.exists
7
+ # asserts("test") { nil }.exists # This would fail
8
+ class ExistsMacro < AssertionMacro
9
+ register :exists
10
+
11
+ def evaluate(actual)
12
+ !actual.nil? ? pass("is not nil") : fail("expected a non-nil value")
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ module Riot
2
+ # Asserts the result contains the expected element
3
+ # asserts("a string") { "world" }.includes('o')
4
+ # asserts("an array") { [1,2,3] }.includes(2)
5
+ # asserts("a range") { (1..15) }.includes(10)
6
+ class IncludesMacro < AssertionMacro
7
+ register :includes
8
+
9
+ def evaluate(actual, expected)
10
+ if actual.include?(expected)
11
+ pass("includes #{expected.inspect}")
12
+ else
13
+ fail("expected #{actual.inspect} to include #{expected.inspect}")
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ module Riot
2
+ # Asserts that the result of the test is an object that is a kind of the expected type
3
+ # asserts("test") { "foo" }.kind_of(String)
4
+ # should("test") { "foo" }.kind_of(String)
5
+ class KindOfMacro < AssertionMacro
6
+ register :kind_of
7
+
8
+ def evaluate(actual, expected)
9
+ if actual.kind_of?(expected)
10
+ pass("is a kind of #{expected.inspect}")
11
+ else
12
+ fail("expected kind of #{expected}, not #{actual.class.inspect}")
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ module Riot
2
+ # Asserts that the result of the test equals matches against the proved expression
3
+ # asserts("test") { "12345" }.matches(/\d+/)
4
+ # should("test") { "12345" }.matches(/\d+/)
5
+ class MatchesMacro < AssertionMacro
6
+ register :matches
7
+
8
+ def evaluate(actual, expected)
9
+ expected = %r[#{Regexp.escape(expected)}] if expected.kind_of?(String)
10
+ if actual =~ expected
11
+ pass("matches #{expected.inspect}")
12
+ else
13
+ fail("expected #{expected.inspect} to match #{actual.inspect}")
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,12 @@
1
+ module Riot
2
+ # Asserts that the result of the test is nil
3
+ # asserts("test") { nil }.nil
4
+ # should("test") { nil }.nil
5
+ class NilMacro < AssertionMacro
6
+ register :nil
7
+
8
+ def evaluate(actual)
9
+ actual.nil? ? pass("is nil") : fail("expected nil, not #{actual.inspect}")
10
+ end
11
+ end
12
+ end