micro_test 0.2.8 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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