attest 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +23 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +17 -0
- data/LICENSE +20 -0
- data/README.rdoc +150 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/attest.gemspec +73 -0
- data/bin/attest +37 -0
- data/doodle.txt +142 -0
- data/examples/magic_calculator.rb +50 -0
- data/examples/more/placeholder.rb +28 -0
- data/examples/standard_calculator.rb +28 -0
- data/lib/attest.rb +38 -0
- data/lib/attest/attest_error.rb +7 -0
- data/lib/attest/config.rb +12 -0
- data/lib/attest/core_ext/kernel.rb +34 -0
- data/lib/attest/core_ext/object.rb +5 -0
- data/lib/attest/execution_context.rb +84 -0
- data/lib/attest/expectation_result.rb +28 -0
- data/lib/attest/itself.rb +34 -0
- data/lib/attest/output/basic_output_writer.rb +73 -0
- data/lib/attest/test_container.rb +24 -0
- data/lib/attest/test_object.rb +59 -0
- data/lib/attest/test_parser.rb +38 -0
- metadata +108 -0
data/.document
ADDED
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Alan Skorkin
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
= Attest
|
2
|
+
|
3
|
+
attest (vb) - to affirm the correctness or truth of
|
4
|
+
|
5
|
+
Attest allows you to define spec-like tests inline (within the same file as your actual code) which means almost non-existant overheads to putting some tests around your code. Of course that is just a feature that I wanted, you can just as easily put the tests into a separate file for a more traditional experience. Overall, attest tries to not be too prescriptive regarding the 'right' way to test. You want to test private methods - go ahead, access unexposed instance variables - no worries, pending and disabled tests are first class citizens. Don't like the output format, use a different one or write your own (at least that the plan for the future, currently there is only one output writer :P). You should be allowed to test your code the way you want to, not the way someone else says you have to!
|
6
|
+
|
7
|
+
== A Quick Example
|
8
|
+
|
9
|
+
Currently the functionality is pretty minimal, almost fully described by the example below. But as you can see the features are complete enough to make it a fully fledged testing framework. The upside is, there is not a lot to remember/learn :P. Anyways, here is an example:
|
10
|
+
|
11
|
+
class MagicCalculator
|
12
|
+
def remember_value(value)
|
13
|
+
@value_in_memory = value
|
14
|
+
end
|
15
|
+
|
16
|
+
def increment(value)
|
17
|
+
value + 1
|
18
|
+
end
|
19
|
+
|
20
|
+
def divide(numerator, denominator)
|
21
|
+
numerator/denominator
|
22
|
+
end
|
23
|
+
|
24
|
+
def add(value1, value2)
|
25
|
+
value1 + value2
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
def multiply(x, y)
|
30
|
+
x * y
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
if ENV["attest"]
|
35
|
+
this_tests MagicCalculator do
|
36
|
+
before_all{@calculator = MagicCalculator.new}
|
37
|
+
after_all{@calculator = nil}
|
38
|
+
|
39
|
+
test("a pending test")
|
40
|
+
test("deliberately fail the test"){should_fail}
|
41
|
+
test("a successful empty test"){}
|
42
|
+
test("should NOT raise an error") {should_not_raise{@calculator.increment 4}}
|
43
|
+
test("it should raise an error, don't care what kind") {should_raise {@calculator.divide 5, 0}}
|
44
|
+
test("it should raise a ZeroDivisionError error with a message"){should_raise(ZeroDivisionError){@calculator.divide 5, 0}.with_message(/divided by.*/)}
|
45
|
+
test("adding 5 and 2 does not equal 8") { should_not_be_true{ @calculator.add(5,2) == 8 } }
|
46
|
+
test("this test will be an error when non-existant method called") {should_be_true{ @calculator.non_existant }}
|
47
|
+
test("should be able to call a private method like it was public"){should_be_true{@calculator.multiply(2,2) == 4}}
|
48
|
+
|
49
|
+
test "access an instance variable without explicitly exposing it" do
|
50
|
+
@calculator.remember_value(5)
|
51
|
+
should_be_true {@calculator.value_in_memory == 5}
|
52
|
+
end
|
53
|
+
|
54
|
+
test("multiple expectations in one test") do
|
55
|
+
should_not_raise{@calculator.increment 4}
|
56
|
+
should_raise{ @calculator.non_existant }
|
57
|
+
end
|
58
|
+
test("should not have access to calculator instance since run without setup", nosetup){should_be_true{@calculator == nil}}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
Here are a few things to note. All of that stuff would live in the same file and you would execute it in the following fashion:
|
63
|
+
|
64
|
+
attest -f my_ruby_file.rb
|
65
|
+
|
66
|
+
The if statement with the ENV is necessary so that the test code is comlpetely ignored unless you're running it via the 'attest' executable. If anyone can think of a better way of doing it, I am open to ideas. If you have a bunch of files that live in a directory and you want to run the test that live in all of them:
|
67
|
+
|
68
|
+
attest -d directory_with_ruby_files
|
69
|
+
|
70
|
+
As per usual, to get some help:
|
71
|
+
|
72
|
+
attest --help
|
73
|
+
|
74
|
+
If your file needs other ruby files for it to function (as they often do :)), you will need to set up the requires yourself within the 'if ENV' block. I'll give it more smarts in this area at a later date.
|
75
|
+
|
76
|
+
If you want to put the tests in a separate file, you can do that, but you will still need to wrap them in an 'if ENV' block and set up the 'requires' yourself as well, once again more smarts will hopefully appear at a later date.
|
77
|
+
|
78
|
+
Currently the output produces when running will look something like this:
|
79
|
+
|
80
|
+
>~/projects/attest$ bin/attest -f examples/magic_calculator.rb
|
81
|
+
/home/yellow/projects/attest/examples/magic_calculator.rb:
|
82
|
+
MagicCalculator
|
83
|
+
- a pending test [PENDING]
|
84
|
+
- deliberately fail the test [FAILURE]
|
85
|
+
- a successful empty test
|
86
|
+
- should NOT raise an error
|
87
|
+
- it should raise an error, don't care what kind
|
88
|
+
- it should raise a ZeroDivisionError error with a message
|
89
|
+
- adding 5 and 2 does not equal 8
|
90
|
+
- this test will be an error when non-existant method called [ERROR]
|
91
|
+
|
92
|
+
NoMethodError: undefined method `non_existant' for #<MagicCalculator:0x00000003097b60>
|
93
|
+
|
94
|
+
- should be able to call a private method like it was public
|
95
|
+
- access an instance variable without explicitly exposing it
|
96
|
+
- multiple expectations in one test
|
97
|
+
- should not have access to calculator instance since run without setup
|
98
|
+
|
99
|
+
|
100
|
+
Ran 12 tests: 9 successful, 1 failed, 1 errors, 1 pending
|
101
|
+
|
102
|
+
Currently that's the only kind of output format, I'll whip up some more output formats at a later date and/or give ability to supply your own.
|
103
|
+
|
104
|
+
== Current And Upcoming Features
|
105
|
+
|
106
|
+
- define tests inline
|
107
|
+
- call private methods as if they were public
|
108
|
+
- access instance variables as if they were exposed
|
109
|
+
- a basic output writer
|
110
|
+
- only a few expectation types, should_fail, should_be_true, should_not_be_true, should_raise, should_not_raise - that's it for the moment
|
111
|
+
- setup and teardown for all tests, but can connfigure a test so that no setup is run for it
|
112
|
+
- automatically create a class that includes a module which methods you want to test (if you have a module called MyModule and you want to test its methods buy only after the module has been included in a class, a class called MyModuleClass will be automatically created and will include your module, an object of this new class will be instantiated for you to play with) e.g:
|
113
|
+
|
114
|
+
before_all do
|
115
|
+
@module_class = create_and_include(MyModule)
|
116
|
+
end
|
117
|
+
|
118
|
+
- tries not to pollute core objects too much
|
119
|
+
|
120
|
+
I've got a few things I want to try out in the future, I've already mentioned some of them such as more output writers and ability to define your own as well as giving it more smarts in various areas. More specifically here are some things that are high on my radar:
|
121
|
+
|
122
|
+
- integrating with Mocha to provide at least some test double (i.e. mocking/stubbing) functionality
|
123
|
+
- writing my own test double project in the same general style as attest and integrating with it as well to give another more seamless mock/stub option
|
124
|
+
- default rake task for ability to run via rake rather than having to use the attest executable
|
125
|
+
- output the expectation count, not just the test count in the output report
|
126
|
+
- more smarts around classes that define their own method missing
|
127
|
+
- ability to disable tests and have the output list them as such
|
128
|
+
- write some basic unit tests for the framework using itself as the test framework (i.e. eating own dogfood style)
|
129
|
+
- allow for multiple setup and teardown blocks and ability to specify which tests they are relevant for
|
130
|
+
- haven't yet decided if I nested contexts are a good idea, I find they tend to confuse things
|
131
|
+
- maybe ability to do shared contexts, once again haven't decided if they are a good idea
|
132
|
+
- more types of expectations for convenience, e.g. should_equal etc.
|
133
|
+
- look at providing convenience stuff for web testing since that seems to be a big deal these days, ditto file system testing (requires mucho more thought)
|
134
|
+
|
135
|
+
|
136
|
+
== More Examples
|
137
|
+
|
138
|
+
Go to the examples directory in the code, it contains the above example as well as a couple of others, reasonably easy to understand, as I said not a lot to remember at the moment.
|
139
|
+
|
140
|
+
== Note on Patches/Pull Requests
|
141
|
+
|
142
|
+
* Fork the project.
|
143
|
+
* Make your feature addition or bug fix.
|
144
|
+
* Add tests for it. This is important so I don't break it in a future version unintentionally. (Ok, you can't really do that as yet, but it's coming up :))
|
145
|
+
* Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
146
|
+
* Send me a pull request. Bonus points for topic branches.
|
147
|
+
|
148
|
+
== Copyright
|
149
|
+
|
150
|
+
Copyright (c) 2010 Alan Skorkin. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "attest"
|
8
|
+
gem.summary = %Q{An inline unit testing/spec framework that doesn't force you to follow arbitrary rules}
|
9
|
+
gem.description = %Q{Attest allows you to define spec-like tests inline (within the same file as your actual code) which means almost non-existant overheads to putting some tests around your code. It also tries to not be too prescriptive regarding the 'right' way to test. You want to test private methods - go ahead, access unexposed instance variables - no worries, pending and disabled tests are first class citizens. Don't like the output format, use a different one or write your own. Infact you don't even have to define you tests inline if you prefer the 'traditional' way, separate directory and all. You should be allowed to test your code the way you want to, not the way someone else says you have to!}
|
10
|
+
gem.email = "alan@skorks.com"
|
11
|
+
gem.homepage = "http://github.com/skorks/attest"
|
12
|
+
gem.authors = ["Alan Skorkin"]
|
13
|
+
gem.add_runtime_dependency "trollop"
|
14
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
15
|
+
end
|
16
|
+
Jeweler::GemcutterTasks.new
|
17
|
+
rescue LoadError
|
18
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'rake/testtask'
|
22
|
+
Rake::TestTask.new(:test) do |test|
|
23
|
+
test.libs << 'lib' << 'test'
|
24
|
+
test.pattern = 'test/**/test_*.rb'
|
25
|
+
test.verbose = true
|
26
|
+
end
|
27
|
+
|
28
|
+
begin
|
29
|
+
require 'rcov/rcovtask'
|
30
|
+
Rcov::RcovTask.new do |test|
|
31
|
+
test.libs << 'test'
|
32
|
+
test.pattern = 'test/**/test_*.rb'
|
33
|
+
test.verbose = true
|
34
|
+
end
|
35
|
+
rescue LoadError
|
36
|
+
task :rcov do
|
37
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
task :test => :check_dependencies
|
42
|
+
|
43
|
+
task :default => :test
|
44
|
+
|
45
|
+
require 'rake/rdoctask'
|
46
|
+
Rake::RDocTask.new do |rdoc|
|
47
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
48
|
+
|
49
|
+
rdoc.rdoc_dir = 'rdoc'
|
50
|
+
rdoc.title = "attest #{version}"
|
51
|
+
rdoc.rdoc_files.include('README*')
|
52
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
53
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/attest.gemspec
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{attest}
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Alan Skorkin"]
|
12
|
+
s.date = %q{2010-11-29}
|
13
|
+
s.default_executable = %q{attest}
|
14
|
+
s.description = %q{Attest allows you to define spec-like tests inline (within the same file as your actual code) which means almost non-existant overheads to putting some tests around your code. It also tries to not be too prescriptive regarding the 'right' way to test. You want to test private methods - go ahead, access unexposed instance variables - no worries, pending and disabled tests are first class citizens. Don't like the output format, use a different one or write your own. Infact you don't even have to define you tests inline if you prefer the 'traditional' way, separate directory and all. You should be allowed to test your code the way you want to, not the way someone else says you have to!}
|
15
|
+
s.email = %q{alan@skorks.com}
|
16
|
+
s.executables = ["attest"]
|
17
|
+
s.extra_rdoc_files = [
|
18
|
+
"LICENSE",
|
19
|
+
"README.rdoc"
|
20
|
+
]
|
21
|
+
s.files = [
|
22
|
+
".document",
|
23
|
+
".gitignore",
|
24
|
+
"Gemfile",
|
25
|
+
"Gemfile.lock",
|
26
|
+
"LICENSE",
|
27
|
+
"README.rdoc",
|
28
|
+
"Rakefile",
|
29
|
+
"VERSION",
|
30
|
+
"attest.gemspec",
|
31
|
+
"bin/attest",
|
32
|
+
"doodle.txt",
|
33
|
+
"examples/magic_calculator.rb",
|
34
|
+
"examples/more/placeholder.rb",
|
35
|
+
"examples/standard_calculator.rb",
|
36
|
+
"lib/attest.rb",
|
37
|
+
"lib/attest/attest_error.rb",
|
38
|
+
"lib/attest/config.rb",
|
39
|
+
"lib/attest/core_ext/kernel.rb",
|
40
|
+
"lib/attest/core_ext/object.rb",
|
41
|
+
"lib/attest/execution_context.rb",
|
42
|
+
"lib/attest/expectation_result.rb",
|
43
|
+
"lib/attest/itself.rb",
|
44
|
+
"lib/attest/output/basic_output_writer.rb",
|
45
|
+
"lib/attest/test_container.rb",
|
46
|
+
"lib/attest/test_object.rb",
|
47
|
+
"lib/attest/test_parser.rb"
|
48
|
+
]
|
49
|
+
s.homepage = %q{http://github.com/skorks/attest}
|
50
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
51
|
+
s.require_paths = ["lib"]
|
52
|
+
s.rubygems_version = %q{1.3.7}
|
53
|
+
s.summary = %q{An inline unit testing/spec framework that doesn't force you to follow arbitrary rules}
|
54
|
+
s.test_files = [
|
55
|
+
"examples/more/placeholder.rb",
|
56
|
+
"examples/magic_calculator.rb",
|
57
|
+
"examples/standard_calculator.rb"
|
58
|
+
]
|
59
|
+
|
60
|
+
if s.respond_to? :specification_version then
|
61
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
62
|
+
s.specification_version = 3
|
63
|
+
|
64
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
65
|
+
s.add_runtime_dependency(%q<trollop>, [">= 0"])
|
66
|
+
else
|
67
|
+
s.add_dependency(%q<trollop>, [">= 0"])
|
68
|
+
end
|
69
|
+
else
|
70
|
+
s.add_dependency(%q<trollop>, [">= 0"])
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
data/bin/attest
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
#need this so that bundler doesn't throw an error after library has been installed as gem and the executable is called
|
4
|
+
ENV["BUNDLE_GEMFILE"] = File.expand_path(File.dirname(__FILE__) + "/../Gemfile")
|
5
|
+
ENV["attest"] = "true"
|
6
|
+
require File.expand_path(File.dirname(__FILE__) + "/../lib/attest")
|
7
|
+
|
8
|
+
opts = Trollop::options do
|
9
|
+
banner <<-EOS
|
10
|
+
Usage:
|
11
|
+
attest [options] <filenames>+
|
12
|
+
where [options] are:
|
13
|
+
EOS
|
14
|
+
|
15
|
+
opt :file, "Ruby file with inline tests that should be executed", :type => String
|
16
|
+
opt :directory, "Directory with ruby files which contain inline tests to be executed", :type => String, :default => nil
|
17
|
+
end
|
18
|
+
Trollop::die :file, "Must exist" unless File.exist?(opts[:file]) if opts[:file]
|
19
|
+
Trollop::die :directory, "Must exist" unless File.exist?(opts[:directory]) && File.directory?(opts[:directory]) if opts[:directory]
|
20
|
+
|
21
|
+
Attest.configure do |config|
|
22
|
+
config.output_writer = Attest::Output::BasicOutputWriter.new
|
23
|
+
end
|
24
|
+
|
25
|
+
if opts[:file]
|
26
|
+
Attest.config.current_file = File.join(File.expand_path(opts[:file]))
|
27
|
+
load opts[:file]
|
28
|
+
end
|
29
|
+
|
30
|
+
if opts[:directory]
|
31
|
+
Dir[File.join(File.expand_path(opts[:directory]), "**/*.rb")].each do |file|
|
32
|
+
Attest.config.current_file = file
|
33
|
+
load file
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
Attest.output_writer.summary
|
data/doodle.txt
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
rough features
|
2
|
+
|
3
|
+
able to write single test in the same file as code under test, should not be able to accidentally run the test code
|
4
|
+
able to go through all the ruby files in the project and run any tests that are found
|
5
|
+
able to print progress to the commandline as tests are being run
|
6
|
+
do not pollute the core classes unnecessarily
|
7
|
+
|
8
|
+
able to go through all the ruby files in supplied directory running tests when found
|
9
|
+
|
10
|
+
|
11
|
+
|
12
|
+
|
13
|
+
|
14
|
+
passing a ruby file to attest should print out whether or not there may be any inline tests in the ruby file
|
15
|
+
|
16
|
+
|
17
|
+
most basic
|
18
|
+
- setup and teardown DONE
|
19
|
+
- specify test for regular public method DONE
|
20
|
+
- specify test for a private method DONE
|
21
|
+
- ability to read instance variable of object under test without having to specify a reader DONE
|
22
|
+
- the most basic result output DONE
|
23
|
+
- able to detect when test expectation fails DONE
|
24
|
+
- execute tests for single ruby file if any present DONE
|
25
|
+
- able to detect when test errors out and gracefully say so in output DONE
|
26
|
+
- all object to have a should_equal expectation method DONE
|
27
|
+
- all objects should have a should_not_equal expectation method DONE
|
28
|
+
|
29
|
+
- should be able to specify a test anonymously without providing description
|
30
|
+
- ability to give a test an id as well as description
|
31
|
+
- ability to specify that no setup or teardown should be run for the test
|
32
|
+
- ability to specify multiple setups and teardowns and register specific setups and teardowns to run for specific tests
|
33
|
+
- ability to magically include module in an object and test its methods while it is included in an object
|
34
|
+
- should provide the error message if test errors out DONE
|
35
|
+
- should provide the error trace if a test has errored DONE
|
36
|
+
- all objects have access to a should_raise expectation DONE
|
37
|
+
- should be able to produce a summary of execution at the end, with count of successes, failures and errors DONE
|
38
|
+
- should be able to produce shorthand output while running with a summary at the end (test unit style shorthand), controlled by cli parameter
|
39
|
+
- should have a should_fail expectation, possibly for tests that haven't been implemented aliased to not_implemented, not_implemented_failing, not_implemented_passing
|
40
|
+
- should have a should_be_true expectation type for more flexibility DONE
|
41
|
+
- test that are defined without a block should be tagged as not implemented in the output and in the summary
|
42
|
+
- you can require the library without augmenting objects with extra methods, only when you call do objects get augmented and should test for the fact that a method we're trying to augment with already exists, alternatively, don't ever require this library directly then we don't have an issue DONE
|
43
|
+
|
44
|
+
- the should methods should return an expectation object (itself) DONE
|
45
|
+
- when deliberately failing should not actually print an error but should be a failure with a message instead DONE
|
46
|
+
- the output writer should be a separate class to allow for different writers DONE
|
47
|
+
- work out what methods need to be on the output writer class and hook them in where tests are run DONE
|
48
|
+
- work out what other expectations need to be on the execution context intself (should_raise, should_be_true) DONE
|
49
|
+
- work out how to easily implement negation of the positive expectations on the execution context
|
50
|
+
- hook in configuration into the framework DONE
|
51
|
+
- make it configurable via configuration and command line the name of the method that begins a test context (this_tests)
|
52
|
+
- make it configurable via config and command line the name of the method that would produce an expectation object (itself or should), these are the only methods that extend core for now
|
53
|
+
- try to execute existing method missing and if still an error then execute my method missing and fall back to existing method missing, so that don't accidentally kill method missing functionality that people wrote themselves
|
54
|
+
- produce a short format output writer as well as a basic long format one to make sure that have ability to hook in different output writers
|
55
|
+
- should be able to configure attest via file (attest.config in same directory, or give location of config as command line param, or via .attest file in home dir or via a configuration block in a file under test, each level can override the other)
|
56
|
+
- what expectation methods are actaully needed on the objects in the test methods themselves (should_be_true, should_equal etc) work out negation for these ones as well
|
57
|
+
- what methods should the expectation object have and how to allow to chain the methods and have everything work
|
58
|
+
- all test context should have an id and if one is not provided it should be generated
|
59
|
+
- all test methods should have an id and if one is not provided it should be generated
|
60
|
+
- before and after blocks should be able to refer to tests they are targeting by the test id
|
61
|
+
- test should be able to specify if they don't want the befores and afters to be run for them (this will take precedence over everything else)
|
62
|
+
- need to have a should_fail execution context method, with alias of fail
|
63
|
+
- should be able to call should_be empty on an object where empty is a method on the object, should_equal size 5 where size is a method on the object, i.e. boolean method on the objects gets used as a predicate
|
64
|
+
- a expectation that this object as the same as another object, i.e. exactly the same object not an equal one
|
65
|
+
- for exceptions, should_raise(XyzError) {x.some_call}.with_message(/some message regex/), the XyzError and the regex should be optional DONE
|
66
|
+
- test methods in a module that is built for class inclusion
|
67
|
+
- test free floating methods that don't belong to a class or a module
|
68
|
+
- an anonymous test without description similat to the test pile below but still for only one thing
|
69
|
+
- a test pile method where you want to test random methods or do lots of assertions at the same time without having to think of a name for it, should introspect somehow what is/will be run and work out descriptions from that
|
70
|
+
- ability to define matchers as lambdas would be good i.e. obj.should_be happy where happy is a lambda that returns a boolean value
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
- a rake task to run the tests instead of a separate executable, configuration should be done through the rake task
|
75
|
+
- ability to mock stuff out using some of the popular mocking frameworks, ability to plug in different one by config, including my own by default which hasn't been written yet
|
76
|
+
- potentially later on think about doing nested contexts as well as shared contexts
|
77
|
+
- some way to potentially work out if files are ruby even if no .rb extension or alternatively pass a glob on the command line to match files
|
78
|
+
- allow redefining of the name of the main context method through the command line(e.g. rather than this_tests allow person to name it whatever they want)
|
79
|
+
|
80
|
+
|
81
|
+
- kill the This class and turn it back into a this_tests method on Kernel DONE
|
82
|
+
- merge everything into Master DONE
|
83
|
+
- branch master into a Hooks branch and leave to have a look at later DONE
|
84
|
+
- remove the hooks stuff from master DONE
|
85
|
+
- add the file in which the tests were found to the output of the tests DONE
|
86
|
+
- add functionality to allow a directory with ruby files to be parsed for tests and all tests to be run if any are found, non-recursively DONE
|
87
|
+
- add functionality to allow a directory to be parsed for tests recursively DONE
|
88
|
+
- allow multiple context within the same file to work properly and execute properly DONE
|
89
|
+
- make sure can test methods in a module not just in a class, magically included in a class DONE
|
90
|
+
- make sure can test class methods DONE
|
91
|
+
- make sure can test methods that don't belong to a module or a class DONE
|
92
|
+
- when multiple contexts in the same file, only print the name of the file to the command line once DONE
|
93
|
+
- don't print summaries after every context, only a totals summary at the end DONE
|
94
|
+
- make it so that tests can take an options hash and one of the options should be to not run setup or teardown for the test, perhaps make it not part of the opts hash DONE
|
95
|
+
- should be able to specify a test without a body for it to appear in the output as pending DONE
|
96
|
+
- refactor all example code so it is a bit nicer and more representative
|
97
|
+
- once all the above are done, create some RDOC documentation to describe the basic features and usage of the framework
|
98
|
+
- also fix up the description and extended description, make sure everything looks proper as far as doco is concerned on github etc.
|
99
|
+
- if everything is ok at this point, do a first public release of the gem (give it an appropriately betaish version number)
|
100
|
+
- make sure the gem is available through rubyforge as well as wherever else is appropriate, e.g. gemcutter
|
101
|
+
|
102
|
+
|
103
|
+
- when committing need to make sure there is a valid gemspec
|
104
|
+
- when used with bundler dependencies like trollop don't come trhough so can't execute attest properly get an error instead need to detect if being used via bundler and do a bundle setup etc.
|
105
|
+
|
106
|
+
next release
|
107
|
+
|
108
|
+
- integrate mocha so that can mock using mocha
|
109
|
+
- output calculations of expectations not just tests
|
110
|
+
- allow expectations to be specified directly as tests (perhaps failures should contain the block that has failed)
|
111
|
+
- create a failures only output writer and allow it to be configured
|
112
|
+
- create a test/unit style output writer and allow it to be configured
|
113
|
+
- test out with method missing defined on the class, does it still work ok
|
114
|
+
- make sure if method missing is defined it is tried out first before the new_method_missing on kernel takes over
|
115
|
+
- should be able to disable a test so that it is not executed, but is listed as disabled in the output
|
116
|
+
|
117
|
+
next release 2
|
118
|
+
|
119
|
+
- write and integrate own test double framework (look at rr for syntax suggestions)
|
120
|
+
- create some tests using the framework itself around the framework classes (minimal test suite)
|
121
|
+
- allow expectations to be made on objects themselves
|
122
|
+
- allow for tests to be given ids as well as descriptions
|
123
|
+
- allow for multiple before and after blocks should specify which test to run this block for using ids of tests as parameters
|
124
|
+
|
125
|
+
next release 3
|
126
|
+
- come up with some more useful expectations, both on object and as standalone methods of test
|
127
|
+
- look into rails stuff regarding expectations and idioms etc
|
128
|
+
- should somehow be able to require all the necessary ruby files that are needed when a test is run for the test not to fail, e.g. because ruby files depend on other ruby files
|
129
|
+
|
130
|
+
|
131
|
+
|
132
|
+
what to say in readme
|
133
|
+
give an example of usage
|
134
|
+
talk about how to define tests inline
|
135
|
+
talk about how to execute the tests in one file or directory etc
|
136
|
+
list the currently supported expectations
|
137
|
+
talk about pendind tests
|
138
|
+
talk about the fact that other output writers are coming
|
139
|
+
talk about the fact that you can disable setup for particular tests
|
140
|
+
talk about how to test modules within an included class
|
141
|
+
no nested context as yet, but can define multiple top level ones, no shared tests or contexts as yet
|
142
|
+
mention the examples directory but it is currently crappy
|
@@ -0,0 +1,50 @@
|
|
1
|
+
class MagicCalculator
|
2
|
+
def remember_value(value)
|
3
|
+
@value_in_memory = value
|
4
|
+
end
|
5
|
+
|
6
|
+
def increment(value)
|
7
|
+
value + 1
|
8
|
+
end
|
9
|
+
|
10
|
+
def divide(numerator, denominator)
|
11
|
+
numerator/denominator
|
12
|
+
end
|
13
|
+
|
14
|
+
def add(value1, value2)
|
15
|
+
value1 + value2
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
def multiply(x, y)
|
20
|
+
x * y
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
if ENV["attest"]
|
25
|
+
this_tests MagicCalculator do
|
26
|
+
before_all{@calculator = MagicCalculator.new}
|
27
|
+
after_all{@calculator = nil}
|
28
|
+
|
29
|
+
test("a pending test")
|
30
|
+
test("deliberately fail the test"){should_fail}
|
31
|
+
test("a successful empty test"){}
|
32
|
+
test("should NOT raise an error") {should_not_raise{@calculator.increment 4}}
|
33
|
+
test("it should raise an error, don't care what kind") {should_raise {@calculator.divide 5, 0}}
|
34
|
+
test("it should raise a ZeroDivisionError error with a message"){should_raise(ZeroDivisionError){@calculator.divide 5, 0}.with_message(/divided by.*/)}
|
35
|
+
test("adding 5 and 2 does not equal 8") { should_not_be_true{ @calculator.add(5,2) == 8 } }
|
36
|
+
test("this test will be an error when non-existant method called") {should_be_true{ @calculator.non_existant }}
|
37
|
+
test("should be able to call a private method like it was public"){should_be_true{@calculator.multiply(2,2) == 4}}
|
38
|
+
|
39
|
+
test "access an instance variable without explicitly exposing it" do
|
40
|
+
@calculator.remember_value(5)
|
41
|
+
should_be_true {@calculator.value_in_memory == 5}
|
42
|
+
end
|
43
|
+
|
44
|
+
test("multiple expectations in one test") do
|
45
|
+
should_not_raise{@calculator.increment 4}
|
46
|
+
should_raise{ @calculator.non_existant }
|
47
|
+
end
|
48
|
+
test("should not have access to calculator instance since run without setup", nosetup){should_be_true{@calculator == nil}}
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class Placeholder1
|
2
|
+
def divide(x,y)
|
3
|
+
x/y
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
class Placeholder2
|
8
|
+
def multiply(x,y)
|
9
|
+
x*y
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def random_method(x)
|
14
|
+
x+x
|
15
|
+
end
|
16
|
+
|
17
|
+
if ENV["attest"]
|
18
|
+
this_tests Placeholder1 do
|
19
|
+
test("divide successful"){should_be_true{Placeholder1.new.divide(16, 4) == 4}}
|
20
|
+
test("divide by zero"){should_raise{Placeholder1.new.divide(5,0)}}
|
21
|
+
end
|
22
|
+
this_tests Placeholder2 do
|
23
|
+
test("multiply") {should_be_true{Placeholder2.new.multiply(2,3)==6}}
|
24
|
+
end
|
25
|
+
this_tests "random methods" do
|
26
|
+
test("random method"){should_be_true{random_method("abc") == "abcabc"}}
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class StandardCalculator
|
2
|
+
def self.plus(x, y)
|
3
|
+
x + y
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.minus(x, y)
|
7
|
+
x - y
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module CalcMethods
|
12
|
+
def double(x)
|
13
|
+
2 * x
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
if ENV["attest"]
|
18
|
+
this_tests "another class with claculations" do
|
19
|
+
test("adding two numbers") {should_be_true{StandardCalculator.plus(5,11) == 16}}
|
20
|
+
test("subtracting two numbers"){should_not_be_true{StandardCalculator.minus(10,5) == 4}}
|
21
|
+
end
|
22
|
+
|
23
|
+
this_tests CalcMethods do
|
24
|
+
before_all { @module_class = create_and_include(CalcMethods) }
|
25
|
+
|
26
|
+
test("magically instance of a class that will include the module"){should_be_true{@module_class.double(5)==10}}
|
27
|
+
end
|
28
|
+
end
|
data/lib/attest.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "bundler"
|
3
|
+
Bundler.setup(:default)
|
4
|
+
|
5
|
+
require "trollop"
|
6
|
+
require "singleton"
|
7
|
+
require 'attest/config'
|
8
|
+
require 'attest/core_ext/kernel'
|
9
|
+
require 'attest/core_ext/object'
|
10
|
+
|
11
|
+
require 'attest/expectation_result'
|
12
|
+
require 'attest/test_container'
|
13
|
+
require 'attest/test_object'
|
14
|
+
require 'attest/execution_context'
|
15
|
+
require 'attest/test_parser'
|
16
|
+
require 'attest/itself'
|
17
|
+
require 'attest/attest_error'
|
18
|
+
|
19
|
+
require 'attest/output/basic_output_writer'
|
20
|
+
|
21
|
+
module Attest
|
22
|
+
class << self
|
23
|
+
def configure
|
24
|
+
config = Attest::Config.instance
|
25
|
+
block_given? ? yield(config) : config
|
26
|
+
end
|
27
|
+
|
28
|
+
alias :config :configure
|
29
|
+
|
30
|
+
Attest::Config.public_instance_methods(false).each do |name|
|
31
|
+
self.class_eval <<-EOT
|
32
|
+
def #{name}(*args)
|
33
|
+
configure.send("#{name}", *args)
|
34
|
+
end
|
35
|
+
EOT
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Kernel
|
2
|
+
def new_method_missing(name, *args, &block)
|
3
|
+
private_method = false
|
4
|
+
instance_variable = false
|
5
|
+
private_methods.each do |meth|
|
6
|
+
private_method = true if meth == name
|
7
|
+
end
|
8
|
+
instance_variable = true if instance_variable_defined?("@#{name}".to_sym)
|
9
|
+
if private_method
|
10
|
+
send(name, *args, &block)
|
11
|
+
elsif instance_variable
|
12
|
+
self.class.class_eval do
|
13
|
+
attr_reader name.to_sym
|
14
|
+
end
|
15
|
+
send(name, *args, &block)
|
16
|
+
else
|
17
|
+
old_method_missing(name, *args, &block)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
alias_method :old_method_missing, :method_missing
|
21
|
+
alias_method :method_missing, :new_method_missing
|
22
|
+
|
23
|
+
private
|
24
|
+
def this_tests(description="anonymous", &block)
|
25
|
+
container = Attest::TestParser.new(description, block).parse
|
26
|
+
container.execute_all
|
27
|
+
end
|
28
|
+
def current_method
|
29
|
+
caller[0][/`([^']*)'/, 1]
|
30
|
+
end
|
31
|
+
def calling_method
|
32
|
+
caller[1][/`([^']*)'/, 1]
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Attest
|
2
|
+
class ExecutionContext
|
3
|
+
attr_reader :results
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@results = []
|
7
|
+
@subject = self
|
8
|
+
end
|
9
|
+
|
10
|
+
def should_raise(type=nil, &block)
|
11
|
+
result = Attest::ExpectationResult.new
|
12
|
+
begin
|
13
|
+
if block_given?
|
14
|
+
yield
|
15
|
+
end
|
16
|
+
rescue => e
|
17
|
+
result.update(:expected_error => e)
|
18
|
+
if type && type == e.class
|
19
|
+
result.success
|
20
|
+
else
|
21
|
+
result.success
|
22
|
+
end
|
23
|
+
end
|
24
|
+
unless result.success?
|
25
|
+
result.failure
|
26
|
+
end
|
27
|
+
@results << result
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
def with_message(regex)
|
32
|
+
result = @results.last
|
33
|
+
if result.success? && result.attributes[:expected_error]
|
34
|
+
if !(result.attributes[:expected_error].message =~ regex)
|
35
|
+
result.failure
|
36
|
+
end
|
37
|
+
end
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
def should_fail
|
42
|
+
result = Attest::ExpectationResult.new
|
43
|
+
result.failure
|
44
|
+
@results << result
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
def should_be_true(&block)
|
49
|
+
result = Attest::ExpectationResult.new
|
50
|
+
block_return = yield
|
51
|
+
block_return ? result.success : result.failure
|
52
|
+
@results << result
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
def should_not_raise(&block)
|
57
|
+
should_raise(&block)
|
58
|
+
result = @results.last
|
59
|
+
result.success? ? result.failure : result.success
|
60
|
+
self
|
61
|
+
end
|
62
|
+
|
63
|
+
def should_not_be_true(&block)
|
64
|
+
should_be_true(&block)
|
65
|
+
result = @results.last
|
66
|
+
result.success? ? result.failure : result.success
|
67
|
+
self
|
68
|
+
end
|
69
|
+
|
70
|
+
#worker methods
|
71
|
+
def create_and_include(module_class)
|
72
|
+
class_name = "#{module_class}Class"
|
73
|
+
class_instance = Class.new
|
74
|
+
Object.const_set class_name, class_instance
|
75
|
+
Object.const_get(class_name).include(Object.const_get("#{module_class}"))
|
76
|
+
klass = Object.const_get(class_name)
|
77
|
+
klass.new
|
78
|
+
end
|
79
|
+
|
80
|
+
def nosetup
|
81
|
+
true
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Attest
|
2
|
+
class ExpectationResult
|
3
|
+
attr_reader :attributes
|
4
|
+
def initialize(attributes={})
|
5
|
+
@outcome = nil
|
6
|
+
@attributes = attributes
|
7
|
+
end
|
8
|
+
|
9
|
+
[:success, :failure, :error, :pending].each do |status|
|
10
|
+
eval <<-EOT
|
11
|
+
def #{status}
|
12
|
+
@outcome = current_method
|
13
|
+
end
|
14
|
+
def #{status}?
|
15
|
+
current_method.chop == @outcome
|
16
|
+
end
|
17
|
+
EOT
|
18
|
+
end
|
19
|
+
|
20
|
+
def status
|
21
|
+
@outcome
|
22
|
+
end
|
23
|
+
|
24
|
+
def update(attributes={})
|
25
|
+
@attributes.merge!(attributes)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Attest
|
2
|
+
class Itself
|
3
|
+
|
4
|
+
def initialize(object)
|
5
|
+
@itself = object
|
6
|
+
@state = state
|
7
|
+
end
|
8
|
+
|
9
|
+
#when error is set, if it is a child of an exception or is an exception itself then with_message is available, otherwise it is not and shoudl throw a no method
|
10
|
+
|
11
|
+
#def with_message(regex)
|
12
|
+
#raise NoMethodError if !@itself.kind_of?(Exception)
|
13
|
+
#if @itself.message =~ regex
|
14
|
+
#@state = Attest::Itself.STATES[:success]
|
15
|
+
#end
|
16
|
+
#end
|
17
|
+
|
18
|
+
#def should_equal(another_object)
|
19
|
+
#@itself == another_object
|
20
|
+
#end
|
21
|
+
|
22
|
+
#def should_not_equal(another_object)
|
23
|
+
#@itself != another_object
|
24
|
+
#end
|
25
|
+
|
26
|
+
#should_be_true
|
27
|
+
#should_not_be_true
|
28
|
+
#should_equal
|
29
|
+
#should_not_equal
|
30
|
+
#should_be_same
|
31
|
+
#should_not_be_same
|
32
|
+
#with_message if itself is an error object
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Attest
|
2
|
+
module Output
|
3
|
+
class BasicOutputWriter
|
4
|
+
def initialize
|
5
|
+
@containers = []
|
6
|
+
end
|
7
|
+
|
8
|
+
def before_everything
|
9
|
+
end
|
10
|
+
|
11
|
+
def before_context(container)
|
12
|
+
previous_container = @containers.last
|
13
|
+
@containers << container
|
14
|
+
puts "#{container.file}:" unless previous_container && previous_container.file == container.file
|
15
|
+
puts " #{ container.description }"
|
16
|
+
end
|
17
|
+
|
18
|
+
def before_test(test_object)
|
19
|
+
print " - #{test_object.description}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def after_test(test_object)
|
23
|
+
relevant_result = nil
|
24
|
+
test_object.results.each do |result|
|
25
|
+
relevant_result = result if !result.success?
|
26
|
+
end
|
27
|
+
print " [#{relevant_result.status.upcase}]" if relevant_result
|
28
|
+
if relevant_result && relevant_result.error?
|
29
|
+
e = relevant_result.attributes[:unexpected_error]
|
30
|
+
2.times { puts }
|
31
|
+
puts " #{e.class}: #{e.message}"
|
32
|
+
e.backtrace.each do |line|
|
33
|
+
break if line =~ /lib\/attest/
|
34
|
+
puts " #{line} "
|
35
|
+
end
|
36
|
+
end
|
37
|
+
puts
|
38
|
+
end
|
39
|
+
|
40
|
+
def after_context
|
41
|
+
puts
|
42
|
+
end
|
43
|
+
|
44
|
+
def summary
|
45
|
+
return unless @containers.size >= 1
|
46
|
+
tests, success, failure, error, pending = 0, 0, 0, 0, 0
|
47
|
+
@containers.each do |container|
|
48
|
+
container.test_objects.each do |test_object|
|
49
|
+
tests += 1
|
50
|
+
test_object.results.each do |result|
|
51
|
+
if result.success?
|
52
|
+
success += 1
|
53
|
+
elsif result.failure?
|
54
|
+
failure += 1
|
55
|
+
elsif result.error?
|
56
|
+
error += 1
|
57
|
+
elsif result.pending?
|
58
|
+
pending += 1
|
59
|
+
else
|
60
|
+
raise "Errr, WTF!!!"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
puts
|
66
|
+
puts "Ran #{tests} tests: #{success} successful, #{failure} failed, #{error} errors, #{pending} pending"
|
67
|
+
end
|
68
|
+
|
69
|
+
def after_everything
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Attest
|
2
|
+
class TestContainer
|
3
|
+
|
4
|
+
attr_reader :description, :test_objects, :file
|
5
|
+
|
6
|
+
def initialize(description)
|
7
|
+
@file = Attest.current_file
|
8
|
+
@description = description
|
9
|
+
@test_objects = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def add(test)
|
13
|
+
@test_objects << test
|
14
|
+
end
|
15
|
+
|
16
|
+
def execute_all
|
17
|
+
Attest.output_writer.before_context(self)
|
18
|
+
@test_objects.each do |test_object|
|
19
|
+
test_object.run
|
20
|
+
end
|
21
|
+
Attest.output_writer.after_context
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Attest
|
2
|
+
class TestObject
|
3
|
+
attr_reader :description, :results
|
4
|
+
attr_accessor :nosetup
|
5
|
+
def initialize(description, test_block)
|
6
|
+
@description = description
|
7
|
+
@test_block = test_block
|
8
|
+
@before = nil
|
9
|
+
@after = nil
|
10
|
+
@results = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_setup(block)
|
14
|
+
@before = block
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_cleanup(block)
|
18
|
+
@after = block
|
19
|
+
end
|
20
|
+
|
21
|
+
def run
|
22
|
+
Attest.output_writer.before_test(self)
|
23
|
+
error = nil
|
24
|
+
context = Attest::ExecutionContext.new
|
25
|
+
begin
|
26
|
+
Object.class_eval do
|
27
|
+
define_method :itself do
|
28
|
+
subject = self
|
29
|
+
context.instance_eval {@subject = subject}
|
30
|
+
context
|
31
|
+
end
|
32
|
+
end
|
33
|
+
context.instance_eval(&@before) if @before && !nosetup
|
34
|
+
context.instance_eval(&@test_block) if @test_block
|
35
|
+
context.instance_eval(&@after) if @after && !nosetup
|
36
|
+
rescue => e
|
37
|
+
error = e
|
38
|
+
ensure
|
39
|
+
@results = context.results
|
40
|
+
add_unexpected_error_result(error) if error
|
41
|
+
add_pending_result unless @test_block
|
42
|
+
end
|
43
|
+
Attest.output_writer.after_test(self)
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
def add_unexpected_error_result(error)
|
48
|
+
result = Attest::ExpectationResult.new(:unexpected_error => error)
|
49
|
+
result.error
|
50
|
+
@results << result
|
51
|
+
end
|
52
|
+
|
53
|
+
def add_pending_result
|
54
|
+
result = Attest::ExpectationResult.new
|
55
|
+
result.pending
|
56
|
+
@results << result
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Attest
|
2
|
+
class TestParser
|
3
|
+
def initialize(description, block)
|
4
|
+
@description = description
|
5
|
+
@block = block
|
6
|
+
@before = nil
|
7
|
+
@after = nil
|
8
|
+
@tests = {}
|
9
|
+
@nosetup = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def parse
|
13
|
+
self.instance_eval(&@block)
|
14
|
+
test_container = Attest::TestContainer.new(@description)
|
15
|
+
@tests.each_pair do |description, test_block|
|
16
|
+
test_object = TestObject.new(description, test_block)
|
17
|
+
test_object.nosetup = true if @nosetup[description]
|
18
|
+
test_object.add_setup(@before)
|
19
|
+
test_object.add_cleanup(@after)
|
20
|
+
test_container.add(test_object)
|
21
|
+
end
|
22
|
+
test_container
|
23
|
+
end
|
24
|
+
|
25
|
+
def before_all(&block)
|
26
|
+
@before = block
|
27
|
+
end
|
28
|
+
|
29
|
+
def after_all(&block)
|
30
|
+
@after = block
|
31
|
+
end
|
32
|
+
|
33
|
+
def test(description, nosetup=false, &block)
|
34
|
+
@tests[description] = block
|
35
|
+
@nosetup[description] = true if nosetup
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
metadata
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: attest
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Alan Skorkin
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-11-29 00:00:00 +11:00
|
19
|
+
default_executable: attest
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: trollop
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
description: Attest allows you to define spec-like tests inline (within the same file as your actual code) which means almost non-existant overheads to putting some tests around your code. It also tries to not be too prescriptive regarding the 'right' way to test. You want to test private methods - go ahead, access unexposed instance variables - no worries, pending and disabled tests are first class citizens. Don't like the output format, use a different one or write your own. Infact you don't even have to define you tests inline if you prefer the 'traditional' way, separate directory and all. You should be allowed to test your code the way you want to, not the way someone else says you have to!
|
36
|
+
email: alan@skorks.com
|
37
|
+
executables:
|
38
|
+
- attest
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files:
|
42
|
+
- LICENSE
|
43
|
+
- README.rdoc
|
44
|
+
files:
|
45
|
+
- .document
|
46
|
+
- .gitignore
|
47
|
+
- Gemfile
|
48
|
+
- Gemfile.lock
|
49
|
+
- LICENSE
|
50
|
+
- README.rdoc
|
51
|
+
- Rakefile
|
52
|
+
- VERSION
|
53
|
+
- attest.gemspec
|
54
|
+
- bin/attest
|
55
|
+
- doodle.txt
|
56
|
+
- examples/magic_calculator.rb
|
57
|
+
- examples/more/placeholder.rb
|
58
|
+
- examples/standard_calculator.rb
|
59
|
+
- lib/attest.rb
|
60
|
+
- lib/attest/attest_error.rb
|
61
|
+
- lib/attest/config.rb
|
62
|
+
- lib/attest/core_ext/kernel.rb
|
63
|
+
- lib/attest/core_ext/object.rb
|
64
|
+
- lib/attest/execution_context.rb
|
65
|
+
- lib/attest/expectation_result.rb
|
66
|
+
- lib/attest/itself.rb
|
67
|
+
- lib/attest/output/basic_output_writer.rb
|
68
|
+
- lib/attest/test_container.rb
|
69
|
+
- lib/attest/test_object.rb
|
70
|
+
- lib/attest/test_parser.rb
|
71
|
+
has_rdoc: true
|
72
|
+
homepage: http://github.com/skorks/attest
|
73
|
+
licenses: []
|
74
|
+
|
75
|
+
post_install_message:
|
76
|
+
rdoc_options:
|
77
|
+
- --charset=UTF-8
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
hash: 3
|
86
|
+
segments:
|
87
|
+
- 0
|
88
|
+
version: "0"
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
hash: 3
|
95
|
+
segments:
|
96
|
+
- 0
|
97
|
+
version: "0"
|
98
|
+
requirements: []
|
99
|
+
|
100
|
+
rubyforge_project:
|
101
|
+
rubygems_version: 1.3.7
|
102
|
+
signing_key:
|
103
|
+
specification_version: 3
|
104
|
+
summary: An inline unit testing/spec framework that doesn't force you to follow arbitrary rules
|
105
|
+
test_files:
|
106
|
+
- examples/more/placeholder.rb
|
107
|
+
- examples/magic_calculator.rb
|
108
|
+
- examples/standard_calculator.rb
|