micro_test 0.2.8 → 0.3.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.
data/README.md CHANGED
@@ -2,48 +2,101 @@
2
2
 
3
3
  ## The Sinatra of testing frameworks
4
4
 
5
- ### Quick Start
5
+ Testing frameworks often lose their focus and become an end unto themselves.
6
+ MicroTest avoids this pitfall with a relentless focus on simplicity.
6
7
 
7
- Install
8
+ ### Here's what MicroTest brings to the table
8
9
 
9
- ```bash
10
- $ gem install micro_test
11
- ```
10
+ * A simple test API/interface
11
+ * An awesome test + dev workflow using Pry
12
+ * Fast asynchronous test runs with Celluloid
12
13
 
13
- Experiment
14
+ ## The Interface
14
15
 
15
- ```bash
16
- $ mt --demo
17
- $ mt --help
18
- ```
16
+ Everything you need to know about MicroTest's API is outlined here.
17
+
18
+ Its simple by design.
19
+
20
+ <table>
21
+ <tr>
22
+ <td><strong><code>MicroTest::Test</code></strong></td>
23
+ <td>Superclass for all test classes.</td>
24
+ </tr>
25
+ <tr>
26
+ <td><strong><code>test(desc, &block)</code></strong></td>
27
+ <td>
28
+ Defines a test method.
29
+ <ul>
30
+ <li><sr<code>desc</code> - a description of what is being tested</li>
31
+ <li><code>block</code> - a block of code which defines the test</li>
32
+ </ul>
33
+ </td>
34
+ </tr>
35
+ <tr>
36
+ <td><strong><code>assert(value)</code></strong></td>
37
+ <td>
38
+ Verifies the truthiness of a value.
39
+ <ul>
40
+ <li><code>value</code> - the value to assert</li>
41
+ </ul>
42
+ </td>
43
+ </tr>
44
+ <tr>
45
+ <td><strong><code>before(&block)</code></strong></td>
46
+ <td>
47
+ A callback that runs before each test method.
48
+ <ul>
49
+ <li><code>&block</code> - the block of code to execute</li>
50
+ </ul>
51
+ </td>
52
+ </tr>
53
+ <tr>
54
+ <td><strong><code>after(&block)</code></strong></td>
55
+ <td>
56
+ A callback that runs after each test method.
57
+ <ul>
58
+ <li><code>&block</code> - the block of code to execute</li>
59
+ </ul>
60
+ </td>
61
+ </tr>
62
+ </table>
19
63
 
20
- Write
64
+ ## A Full Example
65
+
66
+ The entire public interface is used in this basic example.
21
67
 
22
68
  ```ruby
23
- require 'micro_test'
69
+ class MathTest < MicroTest::Test
70
+
71
+ before do
72
+ # runs before each test method
73
+ end
74
+
75
+ after do
76
+ # runs after each test method
77
+ end
24
78
 
25
- class MyTest < MicroTest::Test
26
- test "some assumption" do
27
- assert true
79
+ test "basic addition" do
80
+ assert 2 + 2 == 4
28
81
  end
82
+
29
83
  end
30
84
  ```
31
85
 
32
- Run
86
+ ## Get Started
33
87
 
34
- ```bash
35
- $ mt
36
- ```
88
+ MicroTest ships with a demo so you can kick the tires.
37
89
 
38
- Want BDD?
90
+ * Install `$ gem install micro_test`
91
+ * Help `$ mt --help`
92
+ * Demo `$ mt --demo`
39
93
 
40
- ```ruby
41
- class DescribeMyObject < MicroTest::Test
42
- test "that a feature does something" do
43
- assert true
44
- end
45
- end
46
- ```
94
+ Try some of the more advanced features.
95
+
96
+ * Pry workflow `$ mt --demo --pry`
97
+ * Async test run `$ mt --demo --async`
47
98
 
99
+ MicroTest is small & unobtrusive. It can run parallel with other test frameworks,
100
+ and can be introduced to existing projects with minimal effort.
48
101
 
49
- [See the product page for more details.](http://hopsoft.github.com/micro_test/)
102
+ Start testing today!
data/bin/mt CHANGED
@@ -1,26 +1,83 @@
1
1
  #!/usr/bin/env ruby
2
- require "rubygems"
3
- require "slop"
2
+ require "optparse"
3
+
4
4
  lib_path = File.expand_path(File.join(File.dirname(__FILE__), "..", "lib"))
5
5
  require File.join(lib_path, "micro_test")
6
- require File.join(lib_path, "micro_test", "color")
7
-
8
6
  include MicroTest::Color
9
7
 
10
- opts = Slop.parse(:strict => true, :help => true) do
11
- formatters = Dir[File.join(lib_path, "micro_test", "formatters", "*.rb")].map {|f| f[f.rindex("/") + 1..-4]}
12
- banner "mt [options]"
13
- on :p, :path, "The path to the test directory or file.", :argument => true
14
- on :f, :formatter, "The name of the formatter to use. [#{green formatters.join(", ")}]", :argument => true
15
- on "fail-fast", "Stops the running of tests after the first test failure."
16
- on :pry, "Starts a pry session whenever tests fail."
17
- on :demo, "Runs the MicroTest test suite."
8
+ # setup the formatters list ---------------------------------------------------
9
+ formatters_path = File.join(lib_path, "micro_test", "formatters")
10
+ formatter_names = Dir[File.join(formatters_path, "*.rb")].reduce([]) do |memo, file|
11
+ if (file =~ /(base_formatter\.rb|_async\.rb)$/).nil?
12
+ name = file[file.rindex("/") + 1..-4]
13
+ name = name.prepend("*") if name == "mt"
14
+ memo << name
15
+ end
16
+ memo
18
17
  end
19
18
 
20
- exit if opts[:help]
19
+ # setup the options -----------------------------------------------------------
20
+ options = {}
21
+ parser = OptionParser.new do |opts|
22
+ opts.banner = "MicroTest Usage: mt [options] /path/to/test/dir_or_file"
23
+
24
+ desc = "Runs tests asynchronously."
25
+ opts.on("-a", "--async", desc) { |value| options[:async] = value }
26
+
27
+ desc = "Runs the MicroTest test suite and some additional demo tests."
28
+ opts.on("--demo", desc) { |value| options[:demo] = value }
29
+
30
+ desc = "The formatter to use. [#{formatter_names.join(", ")}]"
31
+ opts.on("-f", "--formatter [FORMATTER]", desc) do |value|
32
+ options[:formatter] = value
33
+ end
34
+
35
+ desc = "Stops the test run after the first failure. "
36
+ opts.on("--fail-fast", desc) { |value| options[:fail_fast] = value }
37
+
38
+ desc = "Starts a PRY session whenever a test fails. "
39
+ opts.on("--pry", desc) { |value| options[:pry] = value }
40
+
41
+ opts.on("-v", "--version", "Show version.") do
42
+ puts "MicroTest #{MicroTest::VERSION}"
43
+ exit
44
+ end
45
+
46
+ opts.on_tail("-h", "--help", "Show this message.") do
47
+ puts opts
48
+ exit
49
+ end
50
+
51
+ end
52
+ parser.parse!
21
53
 
22
- path = opts[:path] || "test"
23
- path = File.expand_path(File.join(File.dirname(__FILE__), "..", "test")) if opts[:demo]
54
+ # apply rules to the options --------------------------------------------------
55
+ if RUBY_ENGINE == "jruby"
56
+ if options[:pry]
57
+ options[:pry] = nil
58
+ puts red("Unfortunately the pry option is not available on jruby")
59
+ puts red("due to a dependency on pry-stack_explorer & binding_of_caller.")
60
+ puts "However, the pry workflow is awesome. Switch to #{green "mri 1.9"} to try it."
61
+ end
62
+ end
63
+ if RUBY_ENGINE == "rbx"
64
+ if options[:pry]
65
+ options[:pry] = nil
66
+ puts red("Unfortunately the pry option is not available on rubinius at this time")
67
+ puts red("due to weird behavior related to pry & pry-stack_explorer.")
68
+ puts "However, the pry workflow is awesome. Switch to #{green "mri 1.9"} to try it."
69
+ end
70
+ end
71
+ if options[:async] && options[:pry]
72
+ options[:pry] = nil
73
+ puts red("Disabling pry while runing in async mode to avoid interleaved pry session chaos.")
74
+ end
75
+ ENV["MT_DEMO"] = "true" if options[:demo]
76
+
77
+ # setup the test path ---------------------------------------------------------
78
+ path = ARGV.last unless ARGV.last =~ /^-/
79
+ path = File.expand_path(File.join(File.dirname(__FILE__), "..", "test")) if options[:demo]
80
+ path ||= "test"
24
81
  path = File.join(Dir.pwd, path) unless path =~ /^\//
25
82
  unless File.exist?(path)
26
83
  puts "#{path} not found."
@@ -30,52 +87,56 @@ end
30
87
  if path =~ /\.rb$/
31
88
  require path
32
89
  else
33
- Dir[File.join(path, "**", "*.rb")].each do |p|
34
- require p unless p =~ /fail\.rb$/ && opts[:demo].nil?
35
- end
90
+ Dir[File.join(path, "**", "*.rb")].each { |path| require path }
36
91
  end
37
92
 
38
- formatter_name = opts[:formatter] || "default"
39
- formatter_name = "documentation" if formatter_name == "d"
40
- formatter_path = File.join(lib_path, "micro_test", "formatters", formatter_name + ".rb")
41
- unless File.exist?(formatter_path)
42
- puts "#{formatter_path} not found."
93
+ # setup the formatter ---------------------------------------------------------
94
+ formatter_name = options[:formatter] || "mt"
95
+ path = File.join(formatters_path, formatter_name + ".rb")
96
+ if options[:async]
97
+ async_path = path.gsub(/\.rb$/, "_async.rb")
98
+ path = async_path if File.exist?(async_path)
99
+ end
100
+ unless File.exist?(path)
101
+ puts "Formatter not found at #{path}"
43
102
  puts "Please check the formatter name and try again."
44
103
  exit
45
104
  end
46
105
  begin
47
- require formatter_path
106
+ require path
48
107
  formatter = MicroTest.const_get("Formatter").new
49
108
  rescue Exception => ex
50
- puts "Failed to load the formatter."
109
+ puts "Failed to load the formatter defined at #{path}"
51
110
  puts ex.message
52
111
  exit
53
112
  end
54
113
 
55
- MicroTest::Test.add_observer(Class.new do
56
- def self.update(event, arg)
57
- if event == :assert && !arg
58
- binding.pry(:quiet => true) if MicroTest::Runner.options[:pry]
59
- exit if MicroTest::Runner.options[:"fail-fast"]
60
- end
61
- end
62
- end)
114
+ # setup the test runner -------------------------------------------------------
115
+ runner = MicroTest::Runner.new(formatter, options)
116
+
117
+ # setup pry -------------------------------------------------------------------
118
+ if options[:pry]
63
119
 
64
- if opts[:pry]
65
- require "pry"
66
- require "pry-stack_explorer"
120
+ begin
121
+ require "pry"
122
+ require "pry-stack_explorer"
123
+ rescue Exception => e
124
+ puts red("Unable to require pry or pry-stack_explorer!")
125
+ puts yellow("Be sure to add these gems to your Gemfile and bundle if you are using Bundler.")
126
+ exit
127
+ end
67
128
 
68
129
  Pry.config.hooks.add_hook :before_session, :print_instructions do |_, _, _pry_|
69
130
  _pry_.commands.create_command "line", "View the assert line that failed." do
70
131
  def process
71
- _pry_.run_command "up 5"
132
+ _pry_.run_command "up 1"
72
133
  end
73
134
  end
74
135
 
75
136
  puts
76
137
  puts "".ljust(80, "-")
77
138
  puts
78
- puts red " " + MicroTest::Runner.current_test
139
+ puts red " " + runner.active_test.desc
79
140
  puts
80
141
  puts "".ljust(80, "-")
81
142
  puts " Pry session started"
@@ -90,6 +151,4 @@ if opts[:pry]
90
151
  end
91
152
  end
92
153
 
93
- @start = Time.now
94
- MicroTest::Runner.run formatter, opts.to_hash
95
- puts "Completed in #{Time.now - @start} seconds."
154
+ runner.run
data/ext/mkrf_conf.rb ADDED
@@ -0,0 +1,32 @@
1
+ # based on instructions here:
2
+ # http://en.wikibooks.org/wiki/Ruby_Programming/RubyGems#How_to_install_different_versions_of_gems_depending_on_which_version_of_ruby_the_installee_is_using
3
+ require 'rubygems'
4
+ require 'rubygems/command.rb'
5
+ require 'rubygems/dependency_installer.rb'
6
+
7
+ begin
8
+ Gem::Command.build_args = ARGV
9
+ rescue NoMethodError
10
+ end
11
+
12
+ installer = Gem::DependencyInstaller.new
13
+
14
+ begin
15
+ if RUBY_ENGINE != "jruby"
16
+ puts "Installing pry and pry-stack_explorer."
17
+ installer.install "pry"
18
+ installer.install "pry-stack_explorer"
19
+ else
20
+ puts "Platform is java... skip install for pry and pry-stack_explorer."
21
+ end
22
+ rescue Exception => e
23
+ puts e.message
24
+ puts e.backtrace.join("\n")
25
+ exit(1)
26
+ end
27
+
28
+ f = File.open(File.join(File.dirname(__FILE__), "Rakefile"), "w") # create dummy rakefile to indicate success
29
+ f.write("task :default\n")
30
+ f.close
31
+
32
+ exit
@@ -0,0 +1,43 @@
1
+ require File.join(File.dirname(__FILE__), "..", "color")
2
+
3
+ module MicroTest
4
+
5
+ # The base class for formatters.
6
+ # Defines the API that formatters can/should implement
7
+ # to control test run output.
8
+ class BaseFormatter
9
+ include MicroTest::Color
10
+ attr_accessor :passed, :failed, :duration
11
+
12
+ def initialize
13
+ @duration = 0
14
+ @passed = 0
15
+ @failed = 0
16
+ end
17
+
18
+ def before_suite(test_classes)
19
+ end
20
+
21
+ def before_class(test_class)
22
+ end
23
+
24
+ def before_test(test)
25
+ end
26
+
27
+ def after_test(test)
28
+ end
29
+
30
+ def after_class(test_class)
31
+ end
32
+
33
+ def after_results(runner)
34
+ @duration = runner.duration
35
+ @passed = runner.passed
36
+ @failed = runner.failed
37
+ end
38
+
39
+ def after_suite(test_classes)
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,55 @@
1
+ require File.join(File.dirname(__FILE__), "base_formatter")
2
+
3
+ module MicroTest
4
+ class Formatter < MicroTest::BaseFormatter
5
+
6
+ def initialize
7
+ @failures = []
8
+ end
9
+
10
+ def before_class(test_class)
11
+ puts "\n#{test_class.name}"
12
+ end
13
+
14
+ def after_test(test)
15
+ super
16
+ if test.passed?
17
+ puts green(" #{test.desc}")
18
+ else
19
+ test.asserts.each do |assert|
20
+ next if assert[:value]
21
+ @failures << { :test => test, :assert => assert }
22
+ puts red(" #{test.desc}")
23
+ end
24
+ end
25
+ end
26
+
27
+ def print_failures(failures)
28
+ puts
29
+ puts "Failures:"
30
+ puts
31
+ failures.each_with_index do |failure, idx|
32
+ test = failure[:test]
33
+ assert = failure[:assert]
34
+ puts
35
+ puts " #{idx + 1}) #{test.test_class.name} #{test.desc}"
36
+ puts red(" Failure/Error: #{assert[:line].strip}")
37
+ puts cyan(" #{assert[:file_path]}:#{assert[:line_num]}")
38
+ end
39
+ end
40
+
41
+ def after_suite(test_classes)
42
+ print_failures(@failures) if failed > 0
43
+ puts
44
+ msg = "#{passed + failed} examples, #{failed} failures"
45
+ if failed > 0
46
+ puts red(msg)
47
+ else
48
+ puts msg
49
+ end
50
+ puts
51
+ super
52
+ end
53
+
54
+ end
55
+ end
@@ -1,27 +1,16 @@
1
- require File.join(File.dirname(__FILE__), "..", "color")
1
+ require File.join(File.dirname(__FILE__), "base_formatter")
2
2
 
3
3
  module MicroTest
4
- class Formatter
5
- include MicroTest::Color
4
+ class Formatter < MicroTest::BaseFormatter
6
5
 
7
- def initialize
6
+ def after_test(test)
7
+ print(test.passed? ? green(".") : red("."))
8
8
  end
9
9
 
10
- def header
11
- end
12
-
13
- def group(name)
14
- end
15
-
16
- def test(info)
17
- if info[:passed]
18
- print green(".")
19
- else
20
- print red("x")
21
- end
22
- end
23
-
24
- def footer
10
+ def after_suite(test_classes)
11
+ puts
12
+ puts "".ljust(80, "-")
13
+ puts "Finished in #{duration} seconds."
25
14
  puts
26
15
  end
27
16
 
@@ -1,26 +1,15 @@
1
- require File.join(File.dirname(__FILE__), "..", "color")
1
+ require File.join(File.dirname(__FILE__), "base_formatter")
2
2
 
3
3
  module MicroTest
4
- class Formatter
5
- include MicroTest::Color
4
+ class Formatter < MicroTest::BaseFormatter
6
5
 
7
- def initialize
8
- @passed = 0
9
- @failed = 0
10
- end
11
-
12
- def header
13
- end
14
-
15
- def group(name)
16
- end
17
-
18
- def test(info)
19
- info[:passed] ? @passed += 1 : @failed += 1
20
- end
21
-
22
- def footer
23
- puts "Passed: #{green @passed}, Failed: #{red @failed}"
6
+ def after_suite(test_classes)
7
+ puts
8
+ puts "Passed: #{green passed}"
9
+ puts "Failed: #{red failed}"
10
+ puts "".ljust(80, "-")
11
+ puts "Finished in #{duration} seconds."
12
+ puts
24
13
  end
25
14
 
26
15
  end
@@ -0,0 +1,63 @@
1
+ require File.join(File.dirname(__FILE__), "base_formatter")
2
+
3
+ module MicroTest
4
+ class Formatter < MicroTest::BaseFormatter
5
+ def before_class(test_class)
6
+ puts
7
+ puts test_class.name.ljust(80, "-")
8
+ end
9
+
10
+ def after_test(test)
11
+ duration = (test.duration * 10**4).round.to_f / 10**4
12
+ if duration < 0.01
13
+ print yellow(" #{duration.to_s.ljust(6, "0")}")
14
+ else
15
+ print red(" #{duration.to_s.ljust(6, "0")}")
16
+ end
17
+
18
+ if test.passed?
19
+ print green(" #{test.desc}")
20
+ else
21
+ puts red(" #{test.desc}")
22
+ test.failed_asserts.each do |assert|
23
+ puts
24
+ print "".ljust(9)
25
+ puts "#{assert[:file_path]}:#{red(assert[:line_num])}"
26
+ puts "".ljust(9) + "".rjust(71, "-")
27
+ index = assert[:line_num] - 1
28
+ start = index - 2
29
+ start = 0 if start <= 0
30
+ finish = index + 2
31
+ finish = assert[:lines].length - 1 if finish >= assert[:lines].length
32
+ (start..finish).each do |i|
33
+ print "".ljust(9)
34
+ if i == index
35
+ print red((i + 1).to_s.rjust(3, "0"))
36
+ print red("|")
37
+ print red(assert[:lines][i])
38
+ else
39
+ print (i + 1).to_s.rjust(3, "0")
40
+ print "|"
41
+ print assert[:lines][i]
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ puts
48
+ end
49
+
50
+ def after_suite(test_classes)
51
+ puts
52
+ puts "".ljust(80, "-")
53
+ print " #{passed + failed} Tests finished in #{yellow duration} seconds. "
54
+ totals = []
55
+ totals << green("#{passed} Passed") if passed > 0
56
+ totals << red("#{failed} Failed") if failed > 0
57
+ print "(#{totals.join(", ")})"
58
+ puts
59
+ puts
60
+ end
61
+
62
+ end
63
+ end
@@ -0,0 +1,66 @@
1
+ require File.join(File.dirname(__FILE__), "base_formatter")
2
+
3
+ module MicroTest
4
+ class Formatter < MicroTest::BaseFormatter
5
+
6
+ def after_test(test)
7
+ test.passed? ? print(green ".") : print(red ".")
8
+ end
9
+
10
+ def after_suite(test_classes)
11
+ puts
12
+
13
+ test_classes.each do |test_class|
14
+ puts
15
+ puts test_class.name.ljust(80, "-")
16
+
17
+ test_class.tests.each do |test|
18
+ duration = (test.duration * 10**4).round.to_f / 10**4
19
+ print yellow(" #{duration.to_s.ljust(6, "0")}")
20
+
21
+ if test.passed?
22
+ print green(" #{test.desc}")
23
+ else
24
+ puts red(" #{test.desc}")
25
+ test.failed_asserts.each do |assert|
26
+ puts
27
+ print "".ljust(9)
28
+ puts "#{assert[:file_path]}:#{red(assert[:line_num])}"
29
+ puts "".ljust(9) + "".rjust(71, "-")
30
+ index = assert[:line_num] - 1
31
+ start = index - 2
32
+ start = 0 if start <= 0
33
+ finish = index + 2
34
+ finish = assert[:lines].length - 1 if finish >= assert[:lines].length
35
+ (start..finish).each do |i|
36
+ print "".ljust(9)
37
+ if i == index
38
+ print red((i + 1).to_s.rjust(3, "0"))
39
+ print red("|")
40
+ print red(assert[:lines][i])
41
+ else
42
+ print (i + 1).to_s.rjust(3, "0")
43
+ print "|"
44
+ print assert[:lines][i]
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ puts
51
+ end
52
+ end
53
+
54
+ puts
55
+ puts "".ljust(80, "-")
56
+ print " #{passed + failed} Tests finished in #{yellow duration} seconds. "
57
+ totals = []
58
+ totals << green("#{passed} Passed") if passed > 0
59
+ totals << red("#{failed} Failed") if failed > 0
60
+ print "(#{totals.join(", ")})"
61
+ puts
62
+ puts
63
+ end
64
+
65
+ end
66
+ end