fas_test 0.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/README +19 -0
- data/bin/fastest.rb +5 -0
- data/lib/fas_test.rb +197 -0
- data/test/fas_test_tests.rb +71 -0
- metadata +78 -0
data/README
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Auto-discovers test classes in the working directory and runs the tests with
|
2
|
+
basically no boot up time and no configuration. It doesn't matter how you
|
3
|
+
structure your project, instead, simple naming conventions are used:
|
4
|
+
|
5
|
+
- All test files should be named *_tests.rb
|
6
|
+
- All test methods should be named test__*
|
7
|
+
|
8
|
+
Other things you need to know:
|
9
|
+
|
10
|
+
- All test classes should inherit FasTest::TestClass
|
11
|
+
- Your test class can define "setup" and/or "teardown" methods. They do what
|
12
|
+
it sounds like they do.
|
13
|
+
|
14
|
+
Take a look in the test folder to see an example of how the library is used.
|
15
|
+
(fas_test is used to test itself).
|
16
|
+
|
17
|
+
To actually run your tests just invoke fastest.rb from a command line. It will
|
18
|
+
automatically recursively discover all of your test classes within the working
|
19
|
+
directory and run there tests.
|
data/bin/fastest.rb
ADDED
data/lib/fas_test.rb
ADDED
@@ -0,0 +1,197 @@
|
|
1
|
+
module FasTest
|
2
|
+
|
3
|
+
# Used to discover tests and actually run them. Normally this class will be
|
4
|
+
# used by the command line client.
|
5
|
+
class TestRunner
|
6
|
+
|
7
|
+
attr_reader :test_results
|
8
|
+
|
9
|
+
def initialize(verbose = false)
|
10
|
+
@verbose = verbose
|
11
|
+
@assertion_count = 0
|
12
|
+
@test_results = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def run_tests_in_class(test_class)
|
16
|
+
test_instance = init_test_instance(test_class)
|
17
|
+
get_all_test_method_names(test_class).each do |test_method_name|
|
18
|
+
run_test(test_instance, test_method_name)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def init_test_instance(test_class)
|
23
|
+
test_instance = test_class.new
|
24
|
+
test_instance.runner = self
|
25
|
+
return test_instance
|
26
|
+
end
|
27
|
+
|
28
|
+
def increment_assert_count
|
29
|
+
@assertion_count += 1
|
30
|
+
end
|
31
|
+
|
32
|
+
def run_test(test_instance, test_method_name)
|
33
|
+
begin
|
34
|
+
status = TestStatuses::PASS
|
35
|
+
test_instance.setup
|
36
|
+
test_instance.needs_teardown = true
|
37
|
+
test_instance.send(test_method_name)
|
38
|
+
if @verbose
|
39
|
+
puts "Pass: #{test_instance.class.name}::#{test_method_name}"
|
40
|
+
end
|
41
|
+
rescue AssertionException => ex
|
42
|
+
status = TestStatuses::FAIL
|
43
|
+
try_teardown(test_instance)
|
44
|
+
puts "Fail: #{test_instance.class.name}::#{test_method_name}"
|
45
|
+
puts " -> #{ex.message}"
|
46
|
+
rescue Exception => ex
|
47
|
+
status = TestStatuses::CRASH
|
48
|
+
try_teardown(test_instance)
|
49
|
+
puts "Crash: #{test_instance.class.name}::#{test_method_name}"
|
50
|
+
puts " -> #{ex.class.name}: #{ex.message}"
|
51
|
+
pretty_print_stack_trace(ex)
|
52
|
+
rescue Object => obj
|
53
|
+
status = TestStatuses::CRASH
|
54
|
+
try_teardown(test_instance)
|
55
|
+
puts "Crash: #{test_instance.class.name}::#{test_method_name}"
|
56
|
+
puts " -> Raised non-exception: #{obj.to_s}"
|
57
|
+
ensure
|
58
|
+
try_teardown(test_instance)
|
59
|
+
record_test_result(test_instance, test_method_name, status)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def record_test_result(test_instance, test_method_name, status)
|
64
|
+
test_class = test_instance.class
|
65
|
+
key = "#{test_class.name}::#{test_method_name}"
|
66
|
+
@test_results[key] = TestResult.new(test_class, test_method_name, status)
|
67
|
+
end
|
68
|
+
|
69
|
+
def try_teardown(test_instance)
|
70
|
+
if test_instance.needs_teardown
|
71
|
+
begin
|
72
|
+
test_instance.teardown
|
73
|
+
ensure
|
74
|
+
test_instance.needs_teardown = false
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def pretty_print_stack_trace(exception)
|
80
|
+
exception.backtrace.each { |trace| puts " #{trace}"}
|
81
|
+
end
|
82
|
+
|
83
|
+
def run_tests_in_folder(path)
|
84
|
+
load_test_files(path)
|
85
|
+
find_test_classes.each do |test_class|
|
86
|
+
run_tests_in_class(test_class)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def load_test_files(path)
|
91
|
+
Dir[File.join(path, '/**/*_tests.rb')].each { |file_name| load file_name }
|
92
|
+
end
|
93
|
+
|
94
|
+
def get_all_test_method_names(test_class)
|
95
|
+
test_class.instance_methods.find_all { |m| m.start_with?('test__') }
|
96
|
+
end
|
97
|
+
|
98
|
+
def find_test_classes
|
99
|
+
classes = []
|
100
|
+
Module.constants.each do |constant|
|
101
|
+
if constant.end_with? "Tests"
|
102
|
+
classes << eval(constant)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
return classes
|
106
|
+
end
|
107
|
+
|
108
|
+
def get_constant_type(construct)
|
109
|
+
construct_class = construct.class
|
110
|
+
if construct_class == Class
|
111
|
+
return ConstantTypes::CLASS
|
112
|
+
elsif construct_class == Module
|
113
|
+
return ConstantTypes::MODULE
|
114
|
+
else
|
115
|
+
return ConstantTypes::OTHER
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def class_inherits_parent?(child_class, parent_class)
|
120
|
+
child_class.ancestors.any? { |ancestor| ancestor == parent_class }
|
121
|
+
end
|
122
|
+
|
123
|
+
def print_summary
|
124
|
+
pass_count = @test_results.values.find_all { |r| r.status == TestStatuses::PASS }.length
|
125
|
+
fail_count = @test_results.length - pass_count
|
126
|
+
puts "#{pass_count} passed; #{fail_count} failed; #{@assertion_count} assertions"
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
# Types that a ruby constant can refer to
|
132
|
+
class ConstantTypes
|
133
|
+
CLASS = 1
|
134
|
+
MODULE = 2
|
135
|
+
OTHER = 3
|
136
|
+
end
|
137
|
+
|
138
|
+
# Base class for all test suites
|
139
|
+
class TestClass
|
140
|
+
|
141
|
+
attr_writer :runner
|
142
|
+
attr_accessor :needs_teardown
|
143
|
+
|
144
|
+
def setup
|
145
|
+
end
|
146
|
+
|
147
|
+
def teardown
|
148
|
+
end
|
149
|
+
|
150
|
+
def assert_true(expression, msg = "<no msg given>")
|
151
|
+
@runner.increment_assert_count
|
152
|
+
if expression != true
|
153
|
+
raise AssertionException, "expected true but got #{expression.to_s}"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def assert_equal(a, b, msg = "<no msg given>")
|
158
|
+
@runner.increment_assert_count
|
159
|
+
if a != b
|
160
|
+
raise AssertionException, "#{msg} | expected '#{a}' but got '#{b}'"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def fail(msg)
|
165
|
+
raise AssertionException, msg
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
169
|
+
|
170
|
+
# Represents the state of a finished test
|
171
|
+
class TestResult
|
172
|
+
|
173
|
+
attr_reader :test_class
|
174
|
+
attr_reader :method_name
|
175
|
+
attr_reader :status
|
176
|
+
|
177
|
+
def initialize(test_class, method_name, status)
|
178
|
+
@test_class = test_class
|
179
|
+
@method_name = method_name
|
180
|
+
@status = status
|
181
|
+
end
|
182
|
+
|
183
|
+
end
|
184
|
+
|
185
|
+
# Exception thrown when an assertion in a test fails
|
186
|
+
class AssertionException < Exception
|
187
|
+
end
|
188
|
+
|
189
|
+
# Enum of test result states
|
190
|
+
class TestStatuses
|
191
|
+
PASS = 1
|
192
|
+
FAIL = 2
|
193
|
+
CRASH = 3
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
197
|
+
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'lib/fas_test'
|
3
|
+
|
4
|
+
class FasTestTests < FasTest::TestClass
|
5
|
+
|
6
|
+
def setup
|
7
|
+
$stdout = StringIO.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def teardown
|
11
|
+
$stdout = STDOUT
|
12
|
+
end
|
13
|
+
|
14
|
+
def test__redirect_io
|
15
|
+
$stdout = StringIO.new
|
16
|
+
puts 'hello?'
|
17
|
+
assert_equal "hello?\n", $stdout.string
|
18
|
+
end
|
19
|
+
|
20
|
+
def test__run_tests_in_class__results_are_valid
|
21
|
+
runner = FasTest::TestRunner.new
|
22
|
+
runner.run_tests_in_class(FasTestTestClassTests)
|
23
|
+
|
24
|
+
assert_equal(5, runner.test_results.length, "wrong number of tests")
|
25
|
+
|
26
|
+
assert_equal(
|
27
|
+
FasTest::TestStatuses::PASS,
|
28
|
+
runner.test_results['FasTestTests::FasTestTestClassTests::test__pass1'].status)
|
29
|
+
|
30
|
+
assert_equal(
|
31
|
+
FasTest::TestStatuses::FAIL,
|
32
|
+
runner.test_results['FasTestTests::FasTestTestClassTests::test__fail1'].status)
|
33
|
+
|
34
|
+
assert_equal(
|
35
|
+
FasTest::TestStatuses::CRASH,
|
36
|
+
runner.test_results['FasTestTests::FasTestTestClassTests::test__crash1'].status)
|
37
|
+
|
38
|
+
assert_equal(
|
39
|
+
FasTest::TestStatuses::FAIL,
|
40
|
+
runner.test_results['FasTestTests::FasTestTestClassTests::test__assert_true_with_false'].status)
|
41
|
+
|
42
|
+
assert_equal(
|
43
|
+
FasTest::TestStatuses::PASS,
|
44
|
+
runner.test_results['FasTestTests::FasTestTestClassTests::test__asert_true_with_true'].status)
|
45
|
+
end
|
46
|
+
|
47
|
+
class FasTestTestClassTests < FasTest::TestClass
|
48
|
+
|
49
|
+
def test__pass1
|
50
|
+
end
|
51
|
+
|
52
|
+
def test__fail1
|
53
|
+
fail("this is an expected fail")
|
54
|
+
end
|
55
|
+
|
56
|
+
def test__crash1
|
57
|
+
i.am.calling.come.methods.that.dont.exist.so.it.should.crash
|
58
|
+
end
|
59
|
+
|
60
|
+
def test__assert_true_with_false
|
61
|
+
assert_true(false)
|
62
|
+
end
|
63
|
+
|
64
|
+
def test__asert_true_with_true
|
65
|
+
assert_true(true)
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fas_test
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Graeme Hill
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-02-24 00:00:00 -08:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: "Auto-discovers test classes in the working directory and runs the tests with\n\
|
23
|
+
basically no boot up time and no configuration. It doesn't matter how you\n\
|
24
|
+
structure your project, instead, simple naming conventions are used:\n\n - All test files should be named *_tests.rb\n - All test methods should be named test__*\n\n\
|
25
|
+
Other things you need to know:\n\n - All test classes should inherit FasTest::TestClass\n - Your test class can define \"setup\" and/or \"teardown\" methods. They do what\n it sounds like they do.\n\n\
|
26
|
+
Take a look in the test folder to see an example of how the library is used.\n\
|
27
|
+
(fas_test is used to test itself).\n\n\
|
28
|
+
To actually run your tests just invoke fastest.rb from a command line. It will\n\
|
29
|
+
automatically recursively discover all of your test classes within the working\n\
|
30
|
+
directory and run there tests. "
|
31
|
+
email: graemekh@gmail.com
|
32
|
+
executables: []
|
33
|
+
|
34
|
+
extensions: []
|
35
|
+
|
36
|
+
extra_rdoc_files:
|
37
|
+
- README
|
38
|
+
files:
|
39
|
+
- README
|
40
|
+
- bin/fastest.rb
|
41
|
+
- lib/fas_test.rb
|
42
|
+
- test/fas_test_tests.rb
|
43
|
+
has_rdoc: true
|
44
|
+
homepage: http://graemehill.ca
|
45
|
+
licenses: []
|
46
|
+
|
47
|
+
post_install_message:
|
48
|
+
rdoc_options: []
|
49
|
+
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
hash: 3
|
67
|
+
segments:
|
68
|
+
- 0
|
69
|
+
version: "0"
|
70
|
+
requirements: []
|
71
|
+
|
72
|
+
rubyforge_project:
|
73
|
+
rubygems_version: 1.5.2
|
74
|
+
signing_key:
|
75
|
+
specification_version: 3
|
76
|
+
summary: A simple automated testing framework designed to be fast and easy to use.
|
77
|
+
test_files:
|
78
|
+
- test/fas_test_tests.rb
|