testicles 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ doc
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ (The MIT License)
2
+
3
+ Copyright (c) 2009 Nicolas Sanguinetti
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ 'Software'), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,139 @@
1
+ = Testicles, the ballsy test framework
2
+
3
+ require "testicles"
4
+
5
+ class UserTest < Testicles::TestCase
6
+ setup do
7
+ @user = User.new(:name => "John Doe", :email => "john@example.org")
8
+ end
9
+
10
+ test "has a name" do
11
+ assert @user.name == "John Doe"
12
+ end
13
+
14
+ test "has an email" do
15
+ assert @user.email == "john@example.org"
16
+ end
17
+ end
18
+
19
+ Testicles is a small, simple, and easy-to-extend testing framework for ruby. It
20
+ was written as a replacement for Test::Unit, given how awful its code is, and
21
+ how difficult it is to extend in order to add new features.
22
+
23
+ I believe in minimalistic software, which is easily understood, easy to test,
24
+ and specially, easy to extend for third parties. That's where I'm aiming with
25
+ Testicles.
26
+
27
+ == Assertions
28
+
29
+ You probably wonder why the example doesn't have any assertion other than
30
+ +assert+. Why not +assert_equal+, right? Well, the idea is to keep it slim. If
31
+ you want to use more assertions, you're free to define your own. Also, since it
32
+ uses the same assertion API as Test::Unit, you can just require its assertions:
33
+
34
+ require "testicles"
35
+ require "test/unit/assertions"
36
+
37
+ class UserTest < Testicles::TestCase
38
+ include Test::Unit::Assertions
39
+
40
+ # now you can use all of Test::Unit assertions. For free.
41
+ end
42
+
43
+ You can even define rspec-like matchers if you want.
44
+
45
+ == Setup and teardown
46
+
47
+ If you need to run code before or after each test, declare a +setup+ or
48
+ +teardown+ block (respectively.)
49
+
50
+ class UserTest < Testicles::TestCase
51
+ setup do # this runs before each test
52
+ @user = User.create(:name => "John")
53
+ end
54
+
55
+ teardown do # this runs after each test
56
+ @user.destroy
57
+ end
58
+ end
59
+
60
+ +setup+ and +teardown+ blocks are evaluated in the same context as your test,
61
+ which means any instance variables defined in any of them are available in the
62
+ rest.
63
+
64
+ You can also use +global_setup+ and +global_teardown+ to run code only once per
65
+ test case. +global_setup+ blocks will run once before the first test is run, and
66
+ +global_teardown+ will run after all the tests have been run.
67
+
68
+ These methods, however, are dangerous, and should be used with caution, as
69
+ they might introduce dependencies between your tests if you don't write
70
+ your tests properly. Make sure that any state modified by code run in a
71
+ +global_setup+ or +global_teardown+ isn't changed in any of your tests.
72
+
73
+ Also, you should be aware that the code of +global_setup+ and +global_teardown+
74
+ blocks isn't evaluated in the same context as your tests and normal
75
+ +setup+/+teardown+ blocks are, so you can't share instance variables between
76
+ them.
77
+
78
+ == Nested contexts
79
+
80
+ Break down your test into logical chunks with nested contexts:
81
+
82
+ class UserTest < Testicles::TestCase
83
+ setup do
84
+ @user = User.make
85
+ end
86
+
87
+ context "validations" do
88
+ test "validates name" do
89
+ @user.name = nil
90
+ assert !@user.valid?
91
+ end
92
+
93
+ # etc, etc
94
+ end
95
+
96
+ context "something else" do
97
+ # your get the idea
98
+ end
99
+ end
100
+
101
+ Any +setup+ or +teardown+ blocks you defined in a context will run in that
102
+ context and in _any_ other context nested in it.
103
+
104
+ == Pending tests
105
+
106
+ There are two ways of marking a test as pending. You can declare a test with no
107
+ body:
108
+
109
+ class SomeTest < Testicles::TestCase
110
+ test "this test will be marked as pending"
111
+
112
+ test "this tests is also pending"
113
+
114
+ test "this test isn't pending" do
115
+ assert true
116
+ end
117
+ end
118
+
119
+ Or you can call the +pending+ method from inside your test.
120
+
121
+ class SomeTest < Testicles::TestCase
122
+ test "this test is pending" do
123
+ pending "oops, this doesn't work"
124
+ assert false
125
+ end
126
+ end
127
+
128
+ == (Best|Worst) name ever
129
+
130
+ I personally believe the name is full of win. But I understand it's not the
131
+ most marketable name :)
132
+
133
+ If you hate it, suggest a better name. Or just fork it and name your version
134
+ different, it's open source, after all.
135
+
136
+ == Legal
137
+
138
+ Author:: Nicolás Sanguinetti — http://nicolassanguinetti.info
139
+ License:: MIT (see bundled LICENSE file for more info)
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ begin
2
+ require "hanna/rdoctask"
3
+ rescue LoadError
4
+ require "rake/rdoctask"
5
+ end
6
+
7
+ Rake::RDocTask.new do |rd|
8
+ rd.main = "README.rdoc"
9
+ rd.title = "API Documentation for Testicles"
10
+ rd.rdoc_files.include("README.rdoc", "LICENSE", "lib/**/*.rb")
11
+ rd.rdoc_dir = "doc"
12
+ end
13
+
14
+ begin
15
+ require "mg"
16
+ MG.new("testicles.gemspec")
17
+ rescue LoadError
18
+ end
data/lib/testicles.rb ADDED
@@ -0,0 +1,80 @@
1
+ module Testicles
2
+ # Exception raised when an assertion fails. See TestCase#assert
3
+ class AssertionFailed < StandardError; end
4
+
5
+ # Exception raised to mark a test as pending. See TestCase#pending
6
+ class Pending < StandardError; end
7
+
8
+ # Register a new Report. This will make your report available to Testicles,
9
+ # allowing you to run your tests through this report. For example
10
+ #
11
+ # module Testicles
12
+ # class Reports::MyAwesomeReport < Report
13
+ # end
14
+ #
15
+ # add_report :awesomesauce, MyAwesomeReport
16
+ # end
17
+ #
18
+ # See Testicles.report_with to see how to select which report will be used.
19
+ def self.add_report(name, report)
20
+ available_reports[name] = report
21
+ end
22
+
23
+ # Register a test case to be run with Testicles. This is done automatically
24
+ # whenever you subclass Testicles::TestCase, so you probably shouldn't pay
25
+ # much attention to this method.
26
+ def self.add_test_case(test_case)
27
+ available_test_cases << test_case
28
+ end
29
+
30
+ # Set to +false+ to avoid running tests +at_exit+. Default is +true+.
31
+ def self.autorun=(flag)
32
+ @autorun = flag
33
+ end
34
+
35
+ # Checks to see if tests should be run +at_exit+ or not. Default is +true+.
36
+ # See Testicles.autorun=
37
+ def self.autorun?
38
+ !!@autorun
39
+ end
40
+
41
+ # Run all registered test cases through the selected report. You can pass
42
+ # arguments to the Report constructor here.
43
+ #
44
+ # See Testicles.add_test_case and Testicles.report_with
45
+ def self.run_all_tests!(*report_args)
46
+ report = available_reports.fetch(@report).new(*report_args)
47
+ Runner.new(report).run(*available_test_cases)
48
+ end
49
+
50
+ # Select the name of the Report to use when running tests. See
51
+ # Testicles.add_report for more information on registering a report.
52
+ #
53
+ # The default report is Testicles::Reports::Progress
54
+ def self.report_with(name)
55
+ @report = name
56
+ end
57
+
58
+ self.autorun = true
59
+ self.report_with(:progress)
60
+
61
+ def self.available_test_cases
62
+ @test_cases ||= []
63
+ end
64
+ private_class_method :available_test_cases
65
+
66
+ def self.available_reports
67
+ @available_reports ||= {}
68
+ end
69
+ private_class_method :available_reports
70
+ end
71
+
72
+ require "testicles/test_case"
73
+ require "testicles/runner"
74
+ require "testicles/report"
75
+ require "testicles/reports"
76
+ require "testicles/reports/progress"
77
+
78
+ at_exit do
79
+ Testicles.run_all_tests! if Testicles.autorun?
80
+ end
@@ -0,0 +1,173 @@
1
+ module Testicles
2
+ class Report
3
+ # Seconds taken to run the test suite.
4
+ #
5
+ # TODO: this doesn't belong here, it's being set from within the test
6
+ # runner. But we need the value here.
7
+ attr_accessor :time_elapsed
8
+
9
+ # Define an event handler for your report. The different events fired in a
10
+ # report's life cycle are:
11
+ #
12
+ # :start:: Fired by the runner when starting the whole test suite.
13
+ # :enter:: Fired by the runner when starting a particular test case. It
14
+ # will get the test case as an argument.
15
+ # :assertion:: Fired by a test each time an assertion is run.
16
+ # :pass:: Fired by a test after it runs successfully without errors.
17
+ # It will get an instance of PassedTest as an argument.
18
+ # :pending:: Fired by a test which doesn't provide a test block or which
19
+ # calls TestCase#pending. It will get an instance of
20
+ # PendingTest as an argument.
21
+ # :failure:: Fired by a test in which an assertion failed. It will get an
22
+ # instance of FailedTest as an argument.
23
+ # :error:: Fired by a test where an uncaught exception was found. It
24
+ # will get an instance of ErroredTest as an argument.
25
+ # :exit:: Fired by the runner each time a test case finishes. It will
26
+ # take the test case as an argument.
27
+ # :end:: Fired by the runner at the end of the whole test suite.
28
+ #
29
+ # The event handler will receive the report as a first argument, plus any
30
+ # arguments documented above (depending on the event). It will also ensure
31
+ # that any handler for the same event declared on an ancestor class is run.
32
+ def self.on(event, &block)
33
+ define_method(:"on_#{event}") do |*args|
34
+ begin
35
+ super(*args)
36
+ rescue NoMethodError
37
+ end
38
+
39
+ block.call(self, *args)
40
+ end
41
+ end
42
+
43
+ on :pass do |report, passed_test|
44
+ report.passes << passed_test
45
+ end
46
+
47
+ on :pending do |report, pending_test|
48
+ report.pendings << pending_test
49
+ end
50
+
51
+ on :failure do |report, failed_test|
52
+ report.failures << failed_test
53
+ report.failures_and_errors << failed_test
54
+ end
55
+
56
+ on :error do |report, errored_test|
57
+ report.errors << errored_test
58
+ report.failures_and_errors << errored_test
59
+ end
60
+
61
+ on :assertion do |report|
62
+ report.add_assertion
63
+ end
64
+
65
+ # Run a test and report if it passes, fails, or is pending. Takes the name
66
+ # of the test as an argument. You can avoid reporting a passed test by
67
+ # passing +false+ as a second argument.
68
+ def report(name, report_success=true)
69
+ yield
70
+ on_pass(PassedTest.new(name)) if report_success
71
+ rescue Pending => e
72
+ on_pending(PendingTest.new(name, e))
73
+ rescue AssertionFailed => e
74
+ on_failure(FailedTest.new(name, e))
75
+ rescue Exception => e
76
+ on_error(ErroredTest.new(name, e))
77
+ end
78
+
79
+ # List all the tests (as PendingTest instances) that were pending.
80
+ def pendings
81
+ @pendings ||= []
82
+ end
83
+
84
+ # List all the tests (as PassedTest instances) that passed.
85
+ def passes
86
+ @passes ||= []
87
+ end
88
+
89
+ # List all the tests (as FailedTest instances) that failed an assertion.
90
+ def failures
91
+ @failures ||= []
92
+ end
93
+
94
+ # List all the tests (as ErroredTest instances) that raised an unrescued
95
+ # exception.
96
+ def errors
97
+ @errors ||= []
98
+ end
99
+
100
+ # Aggregated and ordered list of tests that either failed an assertion or
101
+ # raised an unrescued exception. Useful for displaying back to the user.
102
+ def failures_and_errors
103
+ @failures_and_errors ||= []
104
+ end
105
+
106
+ # Log an assertion was run (whether it succeeded or failed.)
107
+ def add_assertion
108
+ @assertions ||= 0
109
+ @assertions += 1
110
+ end
111
+
112
+ # Number of assertions run during the report.
113
+ def assertions
114
+ @assertions || 0
115
+ end
116
+
117
+ # Amount ot tests run (whether passed, pending, failed, or errored.)
118
+ def total_tests
119
+ passes.size + failures.size + errors.size + pendings.size
120
+ end
121
+
122
+ # Encapsulate the relevant information for a test that passed.
123
+ class PassedTest
124
+ # Name of the test that passed. Useful for certain reports.
125
+ attr_reader :test_name
126
+
127
+ def initialize(test_name) #:nodoc:
128
+ @test_name = test_name
129
+ end
130
+ end
131
+
132
+ # Encapsulates the relevant information for a test which failed an
133
+ # assertion.
134
+ class FailedTest < PassedTest
135
+ def initialize(test_name, error) #:nodoc:
136
+ super(test_name)
137
+ @error = error
138
+ end
139
+
140
+ # Message with which it failed the assertion
141
+ def error_message
142
+ @error.message
143
+ end
144
+
145
+ # Line of the file where the assertion failed
146
+ def line
147
+ backtrace.first.split(":")[1]
148
+ end
149
+
150
+ # File where the assertion failed
151
+ def file
152
+ backtrace.first.split(":")[0]
153
+ end
154
+
155
+ # Backtrace of the assertion
156
+ def backtrace
157
+ @error.backtrace
158
+ end
159
+ end
160
+
161
+ # Encapsulates the relevant information for a test which raised an
162
+ # unrescued exception.
163
+ class ErroredTest < FailedTest
164
+ end
165
+
166
+ # Encapsulates the relevant information for a test that the user marked as
167
+ # pending.
168
+ class PendingTest < FailedTest
169
+ # Message passed to TestCase#pending, if any.
170
+ alias_method :pending_message, :error_message
171
+ end
172
+ end
173
+ end
@@ -0,0 +1,2 @@
1
+ module Testicles::Reports # :nodoc:
2
+ end
@@ -0,0 +1,111 @@
1
+ module Testicles
2
+ # The +:progress+ report will output a +.+ for each passed test in the suite,
3
+ # a +P+ for each pending test, an +F+ for each test that failed an assertion,
4
+ # and an +E+ for each test that raised an unrescued exception.
5
+ #
6
+ # At the end of the suite it will output a list of all pending tests, with
7
+ # files and line numbers, and after that a list of all failures and errors,
8
+ # which also contains the first 3 lines of the backtrace for each.
9
+ class Reports::Progress < Report
10
+ # Set the stream where the report will be written to. STDOUT by default.
11
+ def initialize(stream=STDOUT)
12
+ @stream = stream
13
+ end
14
+
15
+ on :end do |report|
16
+ report.instance_eval do
17
+ puts
18
+ puts
19
+ report_pending_tests unless pendings.empty?
20
+ report_errors unless errors.empty?
21
+ puts summary
22
+ puts running_time
23
+ end
24
+ end
25
+
26
+ on :pass do |report, pass|
27
+ report.send(:print, ".")
28
+ end
29
+
30
+ on :pending do |report, pending|
31
+ report.send(:print, "P")
32
+ end
33
+
34
+ on :failure do |report, failure|
35
+ report.send(:print, "F")
36
+ end
37
+
38
+ on :error do |report, error|
39
+ report.send(:print, "E")
40
+ end
41
+
42
+ private
43
+
44
+ def running_time
45
+ "Ran in #{time_elapsed} seconds"
46
+ end
47
+
48
+ def report_pending_tests
49
+ puts "Pending tests:"
50
+ puts
51
+
52
+ pad_indexes = pendings.size.to_s.size
53
+ pendings.each_with_index do |pending, index|
54
+ puts " #{pad(index+1, pad_indexes)}) #{pending.test_name} (#{pending.pending_message})"
55
+ puts indent("On line #{pending.line} of `#{pending.file}'", 6 + pad_indexes)
56
+ puts
57
+ end
58
+ end
59
+
60
+ def report_errors
61
+ puts "Failures:"
62
+ puts
63
+
64
+ pad_indexes = failures_and_errors.size.to_s.size
65
+ failures_and_errors.each_with_index do |error, index|
66
+ puts " #{pad(index+1, pad_indexes)}) #{test_type(error)}: `#{error.test_name}' (on line #{error.line} of `#{error.file}')"
67
+ puts indent("With `#{error.error_message}'", 6 + pad_indexes)
68
+ puts indent(error.backtrace[0..2].join("\n"), 6 + pad_indexes)
69
+ puts
70
+ end
71
+ end
72
+
73
+ def summary
74
+ "%d test%s, %d assertion%s (%d passed, %d pending, %d failed, %d errored)" % [total_tests,
75
+ total_tests == 1 ? "" : "s",
76
+ assertions,
77
+ assertions == 1 ? "" : "s",
78
+ passes.size,
79
+ pendings.size,
80
+ failures.size,
81
+ errors.size]
82
+ end
83
+
84
+ def indent(strings, size=2, indent_with=" ")
85
+ Array(strings).map do |str|
86
+ str.to_s.split("\n").map {|s| indent_with * size + s }.join("\n")
87
+ end
88
+ end
89
+
90
+ def pad(str, amount)
91
+ " " * (amount - str.to_s.size) + str.to_s
92
+ end
93
+
94
+ def test_type(test)
95
+ case test # order is important since ErroredTest < FailedTest
96
+ when ErroredTest; "Error"
97
+ when FailedTest; "Failure"
98
+ end
99
+ end
100
+
101
+ def print(*args)
102
+ @stream.print(*args)
103
+ end
104
+
105
+ def puts(*args)
106
+ @stream.puts(*args)
107
+ end
108
+ end
109
+
110
+ add_report :progress, Reports::Progress
111
+ end
@@ -0,0 +1,24 @@
1
+ module Testicles
2
+ class Runner
3
+ # Set up the test runner. Takes in a Report that will be passed to test
4
+ # cases for reporting.
5
+ def initialize(report)
6
+ @report = report
7
+ end
8
+
9
+ # Run a set of test cases, provided as arguments. This will fire relevant
10
+ # events on the runner's report, at the +start+ and +end+ of the test run,
11
+ # and before and after each test case (+enter+ and +exit+.)
12
+ def run(*test_cases)
13
+ @report.on_start if @report.respond_to?(:on_start)
14
+ started_at = Time.now
15
+ test_cases.each do |test_case|
16
+ @report.on_enter(test_case) if @report.respond_to?(:on_enter)
17
+ test_case.run(@report)
18
+ @report.on_exit(test_case) if @report.respond_to?(:on_exit)
19
+ end
20
+ @report.time_elapsed = Time.now - started_at
21
+ @report.on_end if @report.respond_to?(:on_end)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,185 @@
1
+ module Testicles
2
+ class TestCase
3
+ # Run all tests in this context. Takes a Report instance in order to
4
+ # provide output.
5
+ def self.run(result)
6
+ result.report("#{description} global setup", false) { do_global_setup }
7
+ tests.each {|test| test.run(result) }
8
+ result.report("#{description} global teardown", false) { do_global_teardown }
9
+ end
10
+
11
+ # Add a test to be run in this context. This method is aliased as +it+ and
12
+ # +should+ for your comfort.
13
+ def self.test(name, &block)
14
+ tests << new(name, &block)
15
+ end
16
+
17
+ # Add a setup block to be run before each test in this context. This method
18
+ # is aliased as +before+ for your comfort.
19
+ def self.setup(&block)
20
+ define_method :setup do
21
+ super
22
+ instance_eval(&block)
23
+ end
24
+ end
25
+
26
+ # Add a +setup+ block that will be run *once* for the entire test case,
27
+ # before the first test is run.
28
+ #
29
+ # Keep in mind that while +setup+ blocks are evaluated on the context of the
30
+ # test, and thus you can share state between them, your tests will not be
31
+ # able to access instance variables set in a +global_setup+ block.
32
+ #
33
+ # This is usually not needed (and generally using it is a code smell, since
34
+ # you could make a test dependent on the state of other tests, which is a
35
+ # huge problem), but it comes in handy when you need to do expensive
36
+ # operations in your test setup/teardown and the tests won't modify the
37
+ # state set on this operations. For example, creating large amount of
38
+ # records in a database or filesystem, when your tests will only read these
39
+ # records.
40
+ def self.global_setup(&block)
41
+ (class << self; self; end).class_eval do
42
+ define_method :do_global_setup do
43
+ super
44
+ instance_eval(&block)
45
+ end
46
+ end
47
+ end
48
+
49
+ # Add a teardown block to be run after each test in this context. This
50
+ # method is aliased as +after+ for your comfort.
51
+ def self.teardown(&block)
52
+ define_method :teardown do
53
+ instance_eval(&block)
54
+ super
55
+ end
56
+ end
57
+
58
+ # Add a +teardown+ block that will be run *once* for the entire test case,
59
+ # after the last test is run.
60
+ #
61
+ # Keep in mind that while +teardown+ blocks are evaluated on the context of
62
+ # the test, and thus you can share state between the tests and the
63
+ # teardown blocks, you will not be able to access instance variables set in
64
+ # a test from your +global_teardown+ block.
65
+ #
66
+ # See TestCase.global_setup for a discussion on why these methods are best
67
+ # avoided unless you really need them and use them carefully.
68
+ def self.global_teardown(&block)
69
+ (class << self; self; end).class_eval do
70
+ define_method :do_global_teardown do
71
+ instance_eval(&block)
72
+ super
73
+ end
74
+ end
75
+ end
76
+
77
+
78
+ # Define a new test context nested under the current one. All +setup+ and
79
+ # +teardown+ blocks defined on the current context will be inherited by the
80
+ # new context. This method is aliased as +describe+ for your comfort.
81
+ def self.context(description, &block)
82
+ subclass = Class.new(self)
83
+ subclass.class_eval(&block) if block
84
+ subclass.description = "#{self.description} #{description}".strip
85
+ const_set(sanitize_description(description), subclass)
86
+ end
87
+
88
+ class << self
89
+ # Fancy name for your test case, reports can use this to give nice,
90
+ # descriptive output when running your tests.
91
+ attr_accessor :description
92
+
93
+ alias_method :describe, :context
94
+ alias_method :story, :context
95
+
96
+ alias_method :before, :setup
97
+ alias_method :after, :teardown
98
+
99
+ alias_method :before_all, :global_setup
100
+ alias_method :after_all, :global_setup
101
+
102
+ alias_method :it, :test
103
+ alias_method :should, :test
104
+ alias_method :scenario, :test
105
+ end
106
+
107
+ # Initialize a new instance of a single test. This test can be run in
108
+ # isolation by calling TestCase#run.
109
+ def initialize(name, &block)
110
+ @test = block
111
+ @name = name
112
+ end
113
+
114
+ # Run a test in isolation. Any +setup+ and +teardown+ blocks defined for
115
+ # this test case will be run as expected.
116
+ #
117
+ # You need to provide a Report instance to handle errors/pending tests/etc.
118
+ #
119
+ # If the test's block is nil, then the test will be marked as pending and
120
+ # nothing will be run.
121
+ def run(result)
122
+ @result = result
123
+
124
+ result.report(name) do
125
+ pending if test.nil?
126
+
127
+ setup
128
+ instance_eval(&test)
129
+ teardown
130
+ end
131
+ end
132
+
133
+ # Ensure a condition is met. This will raise AssertionFailed if the
134
+ # condition isn't met. You can override the default failure message
135
+ # by passing it as an argument.
136
+ def assert(condition, message="Expected condition to be satisfied")
137
+ @result.on_assertion
138
+ raise AssertionFailed, message unless condition
139
+ end
140
+
141
+ # Make the test be ignored as pending. You can override the default message
142
+ # that will be sent to the report by passing it as an argument.
143
+ def pending(message="Not Yet Implemented")
144
+ raise Pending, message
145
+ end
146
+
147
+ private
148
+
149
+ def setup #:nodoc:
150
+ end
151
+
152
+ def teardown #:nodoc:
153
+ end
154
+
155
+ def test
156
+ @test
157
+ end
158
+
159
+ def name
160
+ @name
161
+ end
162
+
163
+ def self.tests
164
+ @tests ||= []
165
+ end
166
+ private_class_method :tests
167
+
168
+ def self.sanitize_description(description)
169
+ "Test#{description.gsub(/\W+/, ' ').strip.gsub(/(^| )(\w)/) { $2.upcase }}".to_sym
170
+ end
171
+ private_class_method :sanitize_description
172
+
173
+ def self.do_global_setup
174
+ end
175
+ private_class_method :do_global_setup
176
+
177
+ def self.do_global_teardown
178
+ end
179
+ private_class_method :do_global_teardown
180
+
181
+ def self.inherited(child)
182
+ Testicles.add_test_case(child)
183
+ end
184
+ end
185
+ end
data/testicles.gemspec ADDED
@@ -0,0 +1,31 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "testicles"
3
+ s.version = "0.1"
4
+ s.date = "2009-09-11"
5
+
6
+ s.description = "Testicles is a tiny, simple, and easy-to-extend test framework"
7
+ s.summary = s.description
8
+ s.homepage = "http://github.com/foca/testicles"
9
+
10
+ s.authors = ["Nicolás Sanguinetti"]
11
+ s.email = "contacto@nicolassanguinetti.info"
12
+
13
+ s.require_paths = ["lib"]
14
+ s.rubyforge_project = "testicles"
15
+ s.has_rdoc = true
16
+ s.rubygems_version = "1.3.1"
17
+
18
+ s.files = %w[
19
+ .gitignore
20
+ LICENSE
21
+ README.rdoc
22
+ Rakefile
23
+ testicles.gemspec
24
+ lib/testicles.rb
25
+ lib/testicles/test_case.rb
26
+ lib/testicles/runner.rb
27
+ lib/testicles/report.rb
28
+ lib/testicles/reports.rb
29
+ lib/testicles/reports/progress.rb
30
+ ]
31
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: testicles
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.1"
5
+ platform: ruby
6
+ authors:
7
+ - "Nicol\xC3\xA1s Sanguinetti"
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-09-11 00:00:00 -03:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Testicles is a tiny, simple, and easy-to-extend test framework
17
+ email: contacto@nicolassanguinetti.info
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - .gitignore
26
+ - LICENSE
27
+ - README.rdoc
28
+ - Rakefile
29
+ - testicles.gemspec
30
+ - lib/testicles.rb
31
+ - lib/testicles/test_case.rb
32
+ - lib/testicles/runner.rb
33
+ - lib/testicles/report.rb
34
+ - lib/testicles/reports.rb
35
+ - lib/testicles/reports/progress.rb
36
+ has_rdoc: true
37
+ homepage: http://github.com/foca/testicles
38
+ licenses: []
39
+
40
+ post_install_message:
41
+ rdoc_options: []
42
+
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: "0"
50
+ version:
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ requirements: []
58
+
59
+ rubyforge_project: testicles
60
+ rubygems_version: 1.3.2
61
+ signing_key:
62
+ specification_version: 3
63
+ summary: Testicles is a tiny, simple, and easy-to-extend test framework
64
+ test_files: []
65
+