testicles 0.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/.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
+