test-rig 0.0.0

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.
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,5 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Jacob Rothstein
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.
@@ -0,0 +1,115 @@
1
+ h1. Test Rig
2
+
3
+ Test Rig provides two separate modules that work together.
4
+ *Dynamic assertions* are flexible xUnit style matchers called dynamically.
5
+ *Smarter message* supplements test failure messages with a several-line context
6
+ of the test failure.
7
+ This library works well with something like "Context":http://github.com/jeremymcanally/context or Shoulda.
8
+
9
+ h2. Dynamic assertions
10
+
11
+ h3. Background & Philosophy
12
+
13
+ Rspec has never sat well for me. Object.method.should be_true style testing reads neat and is
14
+ really nicely like english. Here's the problem: What we are strictly saying in rspec is that
15
+ testing is the object's responsibility. It's like "hey, you! test yourself."
16
+
17
+ Testing is definitionally an external act. It is manipulating a system by its control points
18
+ (public api) and confirming that the behavior is as expected. Passing a "modal verb":http://en.wikipedia.org/wiki/Modal_verb
19
+ like "should" to the object in question does not describe this behavior.
20
+
21
+ From a more practical perspective, defining rspec behaviors is way too much of
22
+ a pain when we already have an easy way to define methods in ruby.
23
+
24
+ @Test::Unit@, particularly when combined with Context or Shoulda, is good
25
+ enough for most things. The only real issue is that you find yourself defining
26
+ a whole lot of helper methods, like @assert_new_record@, @assert_valid@, @assert_red@
27
+ *Dynamic assertions* fixes this. With the fantastic power of method missing,
28
+ assertions are generated on the fly.
29
+
30
+ One of the really nice things about Rspec is that every matcher automatically
31
+ gets an inverse (logical not) matcher. *Dynamic assertions* provides this as well.
32
+
33
+ h3. Example
34
+
35
+ Let's say we have class named @Entry@ that has a boolean @published?@ and a
36
+ boolean @saved?@ and a record @@foo@ that should be saved but not published.
37
+ Oh, and it should have a user, which we'll represent as the string @"joe"@
38
+
39
+ h4. Positive assertions
40
+
41
+ In Rspec: @@foo.should be_saved@
42
+
43
+ In Test::Unit: @assert @foo.saved?@
44
+
45
+ With Dynamic assertions: @assert_saved @foo@
46
+
47
+ h4. Negative assertions
48
+
49
+ In Rspec: @@foo.should_not be_published@
50
+
51
+ In Test::Unit: <code>assert !@foo.published?</code> or, if you've defined @assert_false@, <code>assert_false @foo.published</code>
52
+
53
+ With Dynamic assertions: <code>assert_not_published @foo</code>
54
+
55
+ h4. Positive equality assertions
56
+
57
+ In Rspec: <code>@foo.user.should == "joe"</code>
58
+
59
+ In Test::Unit: <code>assert_equal "joe", @foo.user</code>
60
+
61
+ With Dynamic assertions: <code>assert_user "joe", @foo</code>
62
+
63
+ h4. Negative equality assertions
64
+
65
+ In Rspec: <code>@foo.user.should == "joe"</code>
66
+
67
+ In Test::Unit: <code>assert_not_equal "joe", @foo.user</code>
68
+
69
+ With Dynamic assertions: <code>assert_not_user "joe", @foo</code>
70
+
71
+ h2. Smarter Message
72
+
73
+ My bane: "false did not equal true" -- What you really want to know is what
74
+ the variable names were or the method calls.
75
+
76
+ If we have the stack trace, we should be able to show you the exact test that
77
+ failed immediately. Smarter Message does exactly that.
78
+
79
+ h3. Example
80
+
81
+ <pre><code>
82
+ #demo_test.rb
83
+ require File.join(File.dirname(__FILE__), "test_helper")
84
+ require 'smarter_message'
85
+
86
+ class DemoTest < Test::Unit::TestCase
87
+ include TestRig::SmarterMessage
88
+ test "demonstration" do
89
+ a = 'foo'
90
+ b = 'bar'
91
+ assert_equal a, b
92
+ end
93
+ end
94
+ </code></pre>
95
+
96
+ <pre><code>
97
+ 1) Failure:
98
+ test demonstration(DemoTest) [./test/demo_test.rb:11]:
99
+ <"foo"> expected but was
100
+ <"bar">.
101
+ ./test/smarter_message_test.rb:11:in `test demonstration'
102
+ 9: a = 'foo'
103
+ 10: b = 'bar'
104
+ --> 11: assert_equal a, b
105
+ 12: end
106
+ 13:
107
+ </code></pre>
108
+
109
+ h3. Settings
110
+
111
+ @TestRig::SmarterMessage.context_lines = 2 #the default@
112
+
113
+ This defaults to two (yielding five lines; two context on each side + the actual line).
114
+ Set this in your test_helper.
115
+ If context_lines is zero, it will just print your failure line.
@@ -0,0 +1,56 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "test-rig"
8
+ gem.summary = %Q{TestRig makes Test::Unit fun again}
9
+ gem.description = %Q{TestRig supplies dynamic assertions and contextual failure messages}
10
+ gem.email = "github@jacobrothstein.com"
11
+ gem.homepage = "http://github.com/jbr/test-rig"
12
+ gem.authors = ["Jacob Rothstein"]
13
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
14
+ end
15
+ Jeweler::GemcutterTasks.new
16
+ rescue LoadError
17
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
18
+ end
19
+
20
+ require 'rake/testtask'
21
+ Rake::TestTask.new(:test) do |test|
22
+ test.libs << 'lib' << 'test'
23
+ test.pattern = 'test/**/*_test.rb'
24
+ test.verbose = true
25
+ end
26
+
27
+ begin
28
+ require 'rcov/rcovtask'
29
+ Rcov::RcovTask.new do |test|
30
+ test.libs << 'test'
31
+ test.pattern = 'test/**/*_test.rb'
32
+ test.verbose = true
33
+ end
34
+ rescue LoadError
35
+ task :rcov do
36
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
37
+ end
38
+ end
39
+
40
+ task :test => :check_dependencies
41
+
42
+ task :default => :test
43
+
44
+ require 'rake/rdoctask'
45
+ Rake::RDocTask.new do |rdoc|
46
+ if File.exist?('VERSION')
47
+ version = File.read('VERSION')
48
+ else
49
+ version = ""
50
+ end
51
+
52
+ rdoc.rdoc_dir = 'rdoc'
53
+ rdoc.title = "test-rig #{version}"
54
+ rdoc.rdoc_files.include('README*')
55
+ rdoc.rdoc_files.include('lib/**/*.rb')
56
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.0
@@ -0,0 +1,11 @@
1
+ require 'test_rig/dynamic_assertions'
2
+ require 'test_rig/smarter_message'
3
+
4
+ module TestRig
5
+ def self.included(klass)
6
+ klass.class_eval do
7
+ include TestRig::DynamicAssertions
8
+ include TestRig::SmarterMessage
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,27 @@
1
+ require 'active_support'
2
+
3
+ module TestRig
4
+ module DynamicAssertions
5
+ def self.included(klass)
6
+ klass.class_eval do
7
+ alias_method_chain :method_missing, :dynamic_assertions
8
+ end
9
+ end
10
+
11
+ def method_missing_with_dynamic_assertions(method, *args)
12
+ if method.to_s =~ /^assert_(not_)?([a-z_]+)/
13
+ method_name = $2.to_sym
14
+ if args.length == 1 && args.first.respond_to?(:"#{method_name}?")
15
+ actual = args.first.send :"#{method_name}?"
16
+ assert $1 ? !actual : actual
17
+ return
18
+ elsif args.length == 2 && args.last.respond_to?(method_name)
19
+ send :"assert_#{$1}equal", args.first, args.last.send(method_name)
20
+ return
21
+ end
22
+ end
23
+
24
+ method_missing_without_dynamic_assertions(method, *args)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,61 @@
1
+ require 'active_support'
2
+
3
+ module TestRig
4
+ module SmarterMessage
5
+ mattr_accessor :context_lines
6
+ @@context_lines = 2
7
+
8
+ def self.included(klass)
9
+ klass.class_eval do
10
+ alias_method_chain :assert_block, :smarter_message
11
+ end
12
+ end
13
+
14
+ def assert_block_with_smarter_message(*args, &blk)
15
+ assert_block_without_smarter_message(*args, &blk)
16
+ rescue Test::Unit::AssertionFailedError => e
17
+ backtrace = e.backtrace
18
+ relevant_traces = relevant_traces(backtrace)
19
+ raise if relevant_traces.blank?
20
+ new_error = Test::Unit::AssertionFailedError.new e.message + "\n" + message_for_backtrace(backtrace)
21
+ new_error.set_backtrace(relevant_traces)
22
+ raise new_error
23
+ end
24
+
25
+ private
26
+
27
+ def message_for_backtrace(backtrace)
28
+ trace = first_relevant_trace(backtrace)
29
+ line = line_at_backtrace(trace, SmarterMessage.context_lines)
30
+ "#{trace}\n#{line}"
31
+ end
32
+
33
+ def relevant_traces(backtrace)
34
+ backtrace.select { |trace| trace =~ /_test\.rb/}.reject{|trace| trace =~ /#{File.basename(__FILE__)}/}
35
+ end
36
+
37
+ def first_relevant_trace(backtrace)
38
+ backtrace.detect { |trace| trace =~ /_test\.rb/}
39
+ end
40
+
41
+ def pad(number, width)
42
+ "#{" " * (width - number.to_s.size)}#{number}"
43
+ end
44
+
45
+ def line_at_backtrace(trace, context = 0)
46
+ file, line = trace.split(":")
47
+ line = line.to_i - 1
48
+ start, finish = line - context, line + context
49
+ width = [start, finish].map{|n| (n + 1).to_s.size }.max
50
+
51
+ lines = File.open(file).readlines
52
+ (start..finish).map do |line_number|
53
+ if lines[line_number]
54
+ "#{line_number == line.to_i ? "--> " : " "}"\
55
+ "#{pad line_number + 1, width}:" +
56
+ lines[line_number].rstrip
57
+ end
58
+ end.compact.join("\n")
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,54 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{test-rig}
8
+ s.version = "0.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Jacob Rothstein"]
12
+ s.date = %q{2009-10-22}
13
+ s.description = %q{TestRig supplies dynamic assertions and contextual failure messages}
14
+ s.email = %q{github@jacobrothstein.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.textile"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.textile",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "lib/test_rig.rb",
27
+ "lib/test_rig/dynamic_assertions.rb",
28
+ "lib/test_rig/smarter_message.rb",
29
+ "test-rig.gemspec",
30
+ "test/dynamic_assertions_test.rb",
31
+ "test/smarter_message_test.rb",
32
+ "test/test_helper.rb"
33
+ ]
34
+ s.homepage = %q{http://github.com/jbr/test-rig}
35
+ s.rdoc_options = ["--charset=UTF-8"]
36
+ s.require_paths = ["lib"]
37
+ s.rubygems_version = %q{1.3.5}
38
+ s.summary = %q{TestRig makes Test::Unit fun again}
39
+ s.test_files = [
40
+ "test/dynamic_assertions_test.rb",
41
+ "test/smarter_message_test.rb",
42
+ "test/test_helper.rb"
43
+ ]
44
+
45
+ if s.respond_to? :specification_version then
46
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
47
+ s.specification_version = 3
48
+
49
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
50
+ else
51
+ end
52
+ else
53
+ end
54
+ end
@@ -0,0 +1,47 @@
1
+ require File.join(File.dirname(__FILE__), "test_helper")
2
+
3
+ class Entry
4
+ def published?() false end
5
+ def saved?() true end
6
+ def user() "joe" end
7
+ end
8
+
9
+ class DynamicAssertionsTest < Test::Unit::TestCase
10
+ include TestRig::DynamicAssertions
11
+
12
+ def setup
13
+ @entry = Entry.new
14
+ end
15
+
16
+ test "positive assertion" do
17
+ assert_saved @entry
18
+ end
19
+
20
+ test "positive assertion failure" do
21
+ assert_test_failure { assert_not_saved @entry }
22
+ end
23
+
24
+ test "negative assertion" do
25
+ assert_not_published @entry
26
+ end
27
+
28
+ test "negative assertion failure" do
29
+ assert_test_failure { assert_published @entry }
30
+ end
31
+
32
+ test "positive equality assertion" do
33
+ assert_user "joe", @entry
34
+ end
35
+
36
+ test 'positive equality assertion failure' do
37
+ assert_test_failure { assert_user 'sue', @entry }
38
+ end
39
+
40
+ test "negative equality assertion" do
41
+ assert_not_user "sally", @entry
42
+ end
43
+
44
+ test 'negative equality assertion failure' do
45
+ assert_test_failure { assert_not_user 'joe', @entry }
46
+ end
47
+ end
@@ -0,0 +1,38 @@
1
+ require File.join(File.dirname(__FILE__), "test_helper")
2
+
3
+ class SmarterMessageTest < Test::Unit::TestCase
4
+ include TestRig::SmarterMessage
5
+
6
+ test "smarter message includes the relevent snippet" do
7
+ a, b = 'foo', 'bar'
8
+ e = assert_test_failure { assert_equal a, b }
9
+ [
10
+ "<\"foo\"> expected but was\n<\"bar\">.",
11
+ "a, b = 'foo', 'bar'",
12
+ "--> 8",
13
+ "assert_equal a, b"
14
+ ].each {|snippet| assert_match snippet, e.message}
15
+ end
16
+
17
+ test "backtrace includes only relevant lines" do
18
+ e = assert_test_failure { assert false }
19
+ [
20
+ "backtrace includes only relevant lines",
21
+ "#{__FILE__}:18"
22
+ ].each {|snippet| assert_match snippet, e.backtrace.first}
23
+ end
24
+
25
+ test 'with four context lines' do
26
+ TestRig::SmarterMessage.context_lines = context = 4
27
+ e = assert_test_failure { assert false }
28
+ context_output = e.message.split("\n").select{|line| line =~ /^(-->)?\s+[0-9]+:/}
29
+
30
+ assert_equal context * 2 + 1, context_output.size
31
+ assert_match "test 'with four context lines", context_output[2]
32
+ assert_match /-->.+assert false/, context_output[context]
33
+ TestRig::SmarterMessage.context_lines = 2
34
+ end
35
+ end
36
+
37
+
38
+
@@ -0,0 +1,13 @@
1
+ require 'rubygems'
2
+ $:.unshift File.instance_eval { expand_path join(dirname(__FILE__), "..", "lib") }
3
+
4
+ require 'active_support'
5
+ require 'test/unit'
6
+ require 'test_rig'
7
+
8
+ class Test::Unit::TestCase
9
+ def self.test(name, &blk) define_method(:"test #{name}", &blk) end
10
+ def assert_test_failure(&blk)
11
+ assert_raise Test::Unit::AssertionFailedError, &blk
12
+ end
13
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: test-rig
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Jacob Rothstein
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-22 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: TestRig supplies dynamic assertions and contextual failure messages
17
+ email: github@jacobrothstein.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - LICENSE
24
+ - README.textile
25
+ files:
26
+ - .document
27
+ - .gitignore
28
+ - LICENSE
29
+ - README.textile
30
+ - Rakefile
31
+ - VERSION
32
+ - lib/test_rig.rb
33
+ - lib/test_rig/dynamic_assertions.rb
34
+ - lib/test_rig/smarter_message.rb
35
+ - test-rig.gemspec
36
+ - test/dynamic_assertions_test.rb
37
+ - test/smarter_message_test.rb
38
+ - test/test_helper.rb
39
+ has_rdoc: true
40
+ homepage: http://github.com/jbr/test-rig
41
+ licenses: []
42
+
43
+ post_install_message:
44
+ rdoc_options:
45
+ - --charset=UTF-8
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: "0"
59
+ version:
60
+ requirements: []
61
+
62
+ rubyforge_project:
63
+ rubygems_version: 1.3.5
64
+ signing_key:
65
+ specification_version: 3
66
+ summary: TestRig makes Test::Unit fun again
67
+ test_files:
68
+ - test/dynamic_assertions_test.rb
69
+ - test/smarter_message_test.rb
70
+ - test/test_helper.rb