easytest 0.1.1 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e042e91fa6e5c9b2c64188574118fbe3df09705081e9ef999cdf78ce50ba6633
4
- data.tar.gz: 6e556cfb345aba74ec5eef4883b9f2bb33863c041223e8dfe3337f16dc2b3501
3
+ metadata.gz: ea89da5a0f2aba7252ea69be567591284bfcc1f53aa089e4824eb54773881b0b
4
+ data.tar.gz: b9bb01188ce657b918cdffe80cb962fd4e3c011fa9a4556b832d49eeed8fef5d
5
5
  SHA512:
6
- metadata.gz: a064dadb35c5af370821737bca2e59bcbfea6e3b4ecb6179779fe34f0e17998d3778fc4eb978f997b7a876be9a95b13e13798c8a378eb9993e58f804bca2fb17
7
- data.tar.gz: fef643dcd0e347880cb202653575c832edf431f6004216f0c054d5cc7246aade6427638d6b7f434b9c327c15255a55bb9f1927ead6e57c272418738b080540e1
6
+ metadata.gz: 6b1539eeef25d6343a411a05afbfcff6143d9f33798fb5f3d13dbf8f3d742ea79247ced5c5da1a994bc0d77b41787ec8c7477ed06b26664ef30987007291ad26
7
+ data.tar.gz: c5e5ebe81739839785eab40d24a3d4e529d7c33f0d2ac5fd5cc20bf2984b65849d6b99f18b46848f6d17d362dfdc95fb16ae7d0f3403b77913a6b182b0f1fc9a
data/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## 0.2.0
6
+
7
+ - Add many matchers.
8
+
9
+ ## 0.1.2
10
+
11
+ - Fix the `easytest` command exit code.
12
+
5
13
  ## 0.1.1
6
14
 
7
15
  No code changes.
data/README.md CHANGED
@@ -46,7 +46,7 @@ $ bundle exec easytest
46
46
  Expected: 2
47
47
  Received: 3
48
48
 
49
- at test/addition_test.rb:6:in `block in <top (required)>'
49
+ # test/addition_test.rb:6:in `block in <top (required)>'
50
50
 
51
51
 
52
52
  Tests: 1 failed, 0 passed, 1 total (1 files)
data/exe/easytest CHANGED
@@ -5,7 +5,4 @@ $LOAD_PATH << File.join(Dir.pwd, "test")
5
5
 
6
6
  require "easytest"
7
7
 
8
- test_files = Dir.glob("test/**/*_test.rb").filter do |file|
9
- ARGV.empty? || ARGV.any? { |pattern| file.include?(pattern) }
10
- end
11
- test_files.each { |file| load file }
8
+ exit Easytest::CLI.new(ARGV).run
data/lib/easytest/case.rb CHANGED
@@ -15,10 +15,23 @@ module Easytest
15
15
  def run
16
16
  block.call
17
17
  true
18
- rescue UnmatchedError => error
19
- loc = error.backtrace_locations[2]
20
- self.report = Reporter.new(error: error, file: loc.absolute_path, location: loc.to_s).report(name)
18
+ rescue MatchError, FatalError => error
19
+ loc = find_location(error) or raise
20
+
21
+ self.report = Reporter.new(
22
+ name: name,
23
+ error: error,
24
+ file: loc.absolute_path,
25
+ location: loc.to_s,
26
+ ).report or raise
27
+
21
28
  false
22
29
  end
30
+
31
+ private
32
+
33
+ def find_location(error)
34
+ error.backtrace_locations.find { |loc| loc.path.end_with?("_test.rb") }
35
+ end
23
36
  end
24
37
  end
@@ -0,0 +1,29 @@
1
+ module Easytest
2
+ class CLI
3
+ SUCCESS = 0
4
+ FAILURE = 1
5
+
6
+ attr_reader :argv
7
+ attr_reader :start_time
8
+
9
+ def initialize(argv)
10
+ @argv = argv
11
+ @start_time = Time.now
12
+ end
13
+
14
+ def run
15
+ Easytest.start
16
+ setup
17
+ successful = Easytest.run
18
+ successful ? SUCCESS : FAILURE
19
+ end
20
+
21
+ private
22
+
23
+ def setup
24
+ Dir.glob(Easytest.test_files_location)
25
+ .filter { |file| argv.empty? || argv.any? { |pattern| file.include?(pattern) } }
26
+ .each { |test_file| load test_file }
27
+ end
28
+ end
29
+ end
data/lib/easytest/dsl.rb CHANGED
@@ -6,8 +6,8 @@ module Easytest
6
6
  Easytest.add_case Case.new(name: name, file: file, &block)
7
7
  end
8
8
 
9
- def expect(actual)
10
- Expectation.new(actual)
9
+ def expect(actual = nil, &block)
10
+ Expectation.new(actual, &block)
11
11
  end
12
12
  end
13
13
  end
@@ -1,7 +1,9 @@
1
1
  module Easytest
2
2
  class Error < StandardError; end
3
3
 
4
- class UnmatchedError < Error
4
+ class FatalError < Error; end
5
+
6
+ class MatchError < Error
5
7
  attr_reader :actual
6
8
  attr_reader :expected
7
9
 
@@ -1,19 +1,64 @@
1
1
  module Easytest
2
2
  class Expectation
3
- def initialize(actual)
3
+ attr_reader :actual
4
+ attr_reader :block
5
+ attr_reader :negate
6
+ alias negate? negate
7
+
8
+ def initialize(actual, negate: false, &block)
4
9
  @actual = actual
10
+ @block = block
11
+ @negate = negate
12
+ end
13
+
14
+ def not
15
+ self.class.new(actual, negate: true, &block)
5
16
  end
6
17
 
7
18
  def to_eq(expected)
8
- Matcher::Equal.new(actual: @actual, expected: expected).match!
19
+ Matcher::Equal.new(actual: actual, expected: expected, negate: negate).match!
9
20
  end
21
+ alias to_equal to_eq
10
22
 
11
23
  def to_be(expected)
12
- Matcher::Be.new(actual: @actual, expected: expected).match!
24
+ Matcher::Be.new(actual: actual, expected: expected, negate: negate).match!
13
25
  end
14
26
 
15
27
  def to_be_nil
16
- Matcher::BeNil.new(actual: @actual).match!
28
+ Matcher::Nil.new(actual: actual, negate: negate).match!
29
+ end
30
+
31
+ def to_be_true
32
+ Matcher::True.new(actual: actual, negate: negate).match!
33
+ end
34
+
35
+ def to_be_false
36
+ Matcher::False.new(actual: actual, negate: negate).match!
37
+ end
38
+
39
+ def to_be_a(expected)
40
+ Matcher::BeA.new(actual: actual, expected: expected, negate: negate).match!
41
+ end
42
+
43
+ def to_be_kind_of(expected)
44
+ Matcher::KindOf.new(actual: actual, expected: expected, negate: negate).match!
45
+ end
46
+
47
+ def to_be_instance_of(expected)
48
+ Matcher::InstanceOf.new(actual: actual, expected: expected, negate: negate).match!
49
+ end
50
+
51
+ def to_raise(exception_class)
52
+ raise FatalError, "`to_raise` requires a block like `expect { ... }.to_raise`" unless block
53
+ raise FatalError, "`not.to_raise` can cause a false positive, so use `to_not_raise` instead" if negate?
54
+
55
+ Matcher::Raise.new(actual: block, expected: exception_class, negate: negate).match!
56
+ end
57
+
58
+ def to_not_raise
59
+ raise FatalError, "`to_not_raise` requires a block like `expect { ... }.to_not_raise`" unless block
60
+
61
+ Matcher::NotRaise.new(actual: block).match!
17
62
  end
18
63
  end
19
64
  end
@@ -2,9 +2,14 @@ module Easytest
2
2
  module Matcher
3
3
  class Base
4
4
  attr_reader :actual
5
+ attr_reader :expected
6
+ attr_reader :negate
7
+ alias negate? negate
5
8
 
6
- def initialize(actual:)
9
+ def initialize(actual:, expected:, negate: false)
7
10
  @actual = actual
11
+ @expected = expected
12
+ @negate = negate
8
13
  end
9
14
 
10
15
  def match?
@@ -12,8 +17,23 @@ module Easytest
12
17
  end
13
18
 
14
19
  def match!
20
+ matched = match?
21
+ matched = !matched if negate?
22
+ raise_match_error unless matched
23
+ end
24
+
25
+ def message
15
26
  raise NotImplementedError
16
27
  end
28
+
29
+ def build_error_message
30
+ prefix = negate? ? "not " : ""
31
+ "#{prefix}#{message}"
32
+ end
33
+
34
+ def raise_match_error
35
+ raise MatchError.new(message: build_error_message, actual: actual, expected: expected)
36
+ end
17
37
  end
18
38
  end
19
39
  end
@@ -1,21 +1,12 @@
1
1
  module Easytest
2
2
  module Matcher
3
3
  class Be < Base
4
- attr_reader :expected
5
-
6
- def initialize(actual:, expected:)
7
- super(actual: actual)
8
- @expected = expected
9
- end
10
-
11
4
  def match?
12
5
  actual.equal? expected
13
6
  end
14
7
 
15
- def match!
16
- unless match?
17
- raise UnmatchedError.new(message: "should be same", actual: actual, expected: expected)
18
- end
8
+ def message
9
+ "same"
19
10
  end
20
11
  end
21
12
  end
@@ -0,0 +1,13 @@
1
+ module Easytest
2
+ module Matcher
3
+ class BeA < Base
4
+ def match?
5
+ actual.is_a? expected
6
+ end
7
+
8
+ def message
9
+ "be a"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,21 +1,12 @@
1
1
  module Easytest
2
2
  module Matcher
3
3
  class Equal < Base
4
- attr_reader :expected
5
-
6
- def initialize(actual:, expected:)
7
- super(actual: actual)
8
- @expected = expected
9
- end
10
-
11
4
  def match?
12
5
  actual == expected
13
6
  end
14
7
 
15
- def match!
16
- unless match?
17
- raise UnmatchedError.new(message: "should equal", actual: actual, expected: expected)
18
- end
8
+ def message
9
+ "equal"
19
10
  end
20
11
  end
21
12
  end
@@ -0,0 +1,17 @@
1
+ module Easytest
2
+ module Matcher
3
+ class False < Base
4
+ def initialize(actual:, negate:)
5
+ super(actual: actual, expected: false, negate: negate)
6
+ end
7
+
8
+ def match?
9
+ actual == expected
10
+ end
11
+
12
+ def message
13
+ "false"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,13 @@
1
+ module Easytest
2
+ module Matcher
3
+ class InstanceOf < Base
4
+ def match?
5
+ actual.instance_of? expected
6
+ end
7
+
8
+ def message
9
+ "instance of"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Easytest
2
+ module Matcher
3
+ class KindOf < Base
4
+ def match?
5
+ actual.kind_of? expected
6
+ end
7
+
8
+ def message
9
+ "kind of"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,17 @@
1
+ module Easytest
2
+ module Matcher
3
+ class Nil < Base
4
+ def initialize(actual:, negate:)
5
+ super(actual: actual, expected: nil, negate: negate)
6
+ end
7
+
8
+ def match?
9
+ actual.nil?
10
+ end
11
+
12
+ def message
13
+ "nil"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,27 @@
1
+ module Easytest
2
+ module Matcher
3
+ class NotRaise < Base
4
+ def initialize(actual:)
5
+ super(actual: actual, expected: nil)
6
+ end
7
+
8
+ def match?
9
+ begin
10
+ actual.call
11
+ true
12
+ rescue => error
13
+ @raised_error = error
14
+ false
15
+ end
16
+ end
17
+
18
+ def raise_match_error
19
+ raise MatchError.new(message: build_error_message, actual: @raised_error, expected: expected)
20
+ end
21
+
22
+ def message
23
+ "not raise"
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,38 @@
1
+ module Easytest
2
+ module Matcher
3
+ class Raise < Base
4
+ def match?
5
+ begin
6
+ actual.call
7
+ false
8
+ rescue => error
9
+ @raised_error = error
10
+ match_error?(actual: error, expected: expected)
11
+ end
12
+ end
13
+
14
+ def raise_match_error
15
+ raise MatchError.new(message: build_error_message, actual: @raised_error, expected: expected)
16
+ end
17
+
18
+ def message
19
+ "raise"
20
+ end
21
+
22
+ private
23
+
24
+ def match_error?(actual:, expected:)
25
+ case expected
26
+ when Class
27
+ actual.class == expected
28
+ when String
29
+ actual.message == expected
30
+ when Regexp
31
+ actual.message.match? expected
32
+ else
33
+ raise TypeError.new "Class, String, or Regexp is allowed: #{expected}"
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,17 @@
1
+ module Easytest
2
+ module Matcher
3
+ class True < Base
4
+ def initialize(actual:, negate:)
5
+ super(actual: actual, expected: true, negate: negate)
6
+ end
7
+
8
+ def match?
9
+ actual == expected
10
+ end
11
+
12
+ def message
13
+ "true"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,19 +1,46 @@
1
1
  module Easytest
2
2
  class Reporter
3
- def initialize(error:, file:, location:)
3
+ attr_reader :name
4
+ attr_reader :error
5
+ attr_reader :file
6
+ attr_reader :location
7
+
8
+ def initialize(name:, error:, file:, location:)
9
+ @name = name
4
10
  @error = error
5
11
  @file = file
6
12
  @location = location
7
13
  end
8
14
 
9
- def report(test_name)
15
+ def report
16
+ case error
17
+ when MatchError
18
+ report_match_error
19
+ when FatalError
20
+ report_fatal_error
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def report_match_error
27
+ <<~MSG
28
+ #{Rainbow("● #{name}").red.bright} #{Rainbow("(#{error.message})").dimgray}
29
+
30
+ #{Rainbow("Expected: #{error.expected.inspect}").green}
31
+ #{Rainbow("Received: #{error.actual.inspect}").red}
32
+
33
+ #{Rainbow("# #{location}").dimgray}
34
+ MSG
35
+ end
36
+
37
+ def report_fatal_error
10
38
  <<~MSG
11
- #{Rainbow("● #{test_name}").red.bright} #{Rainbow("(#{@error.message})").dimgray}
39
+ #{Rainbow("● #{name}").red.bright}
12
40
 
13
- #{Rainbow("Expected: #{@error.expected.inspect}").green}
14
- #{Rainbow("Received: #{@error.actual.inspect}").red}
41
+ #{Rainbow(error.message).red}
15
42
 
16
- #{Rainbow("at #{@location}").dimgray}
43
+ #{Rainbow("# #{location}").dimgray}
17
44
  MSG
18
45
  end
19
46
  end
@@ -0,0 +1,94 @@
1
+ module Easytest
2
+ class Runner
3
+ attr_reader :start_time
4
+ attr_accessor :passed_count
5
+ attr_accessor :failed_count
6
+ attr_accessor :file_count
7
+
8
+ def initialize(start_time: Time.now)
9
+ @start_time = start_time
10
+ @passed_count = 0
11
+ @failed_count = 0
12
+ @file_count = 0
13
+ end
14
+
15
+ def run
16
+ cases_by_file = cases.group_by { |c| c.file }
17
+ cases_by_file.each do |file, cases|
18
+ self.file_count += 1
19
+ reports = []
20
+
21
+ cases.each do |c|
22
+ passed = c.run
23
+
24
+ if passed
25
+ self.passed_count += 1
26
+ else
27
+ self.failed_count += 1
28
+ reports << c.report.gsub(/^/, " ")
29
+ end
30
+ end
31
+
32
+ link = Utils.terminal_hyperlink(file)
33
+ if reports.empty?
34
+ puts "#{Rainbow(" PASS ").bright.bg(:green)} #{link}"
35
+ else
36
+ puts "#{Rainbow(" FAIL ").bright.bg(:red)} #{link}"
37
+ reports.each { |report| puts report ; puts "" }
38
+ end
39
+ end
40
+
41
+ if no_tests?
42
+ puts Rainbow("Oops. No tests found!").red.bright
43
+ puts ""
44
+ puts "Write `#{Easytest.test_files_location}` files to include at least one `test` block,"
45
+ puts "or specify a matching pattern to the `easytest` command."
46
+ false
47
+ else
48
+ puts ""
49
+ puts " #{Rainbow('Tests:').bright} #{summary}"
50
+ puts " #{Rainbow('Time:').bright} #{elapsed_time.round(5)} seconds"
51
+ all_passed?
52
+ end
53
+ end
54
+
55
+ def cases
56
+ @cases ||= []
57
+ end
58
+
59
+ def add_case(new_case)
60
+ cases << new_case
61
+ end
62
+
63
+ private
64
+
65
+ def total_count
66
+ passed_count + failed_count
67
+ end
68
+
69
+ def all_passed?
70
+ failed_count == 0
71
+ end
72
+
73
+ def no_tests?
74
+ total_count == 0
75
+ end
76
+
77
+ def elapsed_time
78
+ Time.now - start_time
79
+ end
80
+
81
+ def summary
82
+ summary = ""
83
+
84
+ if failed_count == 0
85
+ summary << Rainbow("#{passed_count} passed").green.bright
86
+ else
87
+ summary << Rainbow("#{failed_count} failed").red.bright
88
+ summary << ", #{Rainbow("#{passed_count} passed").green.bright}"
89
+ end
90
+
91
+ summary << ", #{total_count} total #{Rainbow("(#{file_count} files)").dimgray}"
92
+ end
93
+ end
94
+ end
@@ -1,3 +1,3 @@
1
1
  module Easytest
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/easytest.rb CHANGED
@@ -4,83 +4,41 @@ require "rainbow"
4
4
  require_relative "easytest/version"
5
5
 
6
6
  require_relative "easytest/case"
7
+ require_relative "easytest/cli"
7
8
  require_relative "easytest/dsl"
8
9
  require_relative "easytest/errors"
9
10
  require_relative "easytest/expectation"
11
+ require_relative "easytest/reporter"
12
+ require_relative "easytest/runner"
13
+ require_relative "easytest/utils"
14
+
15
+ # Matcher
10
16
  require_relative "easytest/matcher/base"
11
17
  require_relative "easytest/matcher/be"
12
- require_relative "easytest/matcher/be_nil"
18
+ require_relative "easytest/matcher/be_a"
13
19
  require_relative "easytest/matcher/equal"
14
- require_relative "easytest/reporter"
15
- require_relative "easytest/utils"
20
+ require_relative "easytest/matcher/false"
21
+ require_relative "easytest/matcher/instance_of"
22
+ require_relative "easytest/matcher/kind_of"
23
+ require_relative "easytest/matcher/nil"
24
+ require_relative "easytest/matcher/not_raise"
25
+ require_relative "easytest/matcher/raise"
26
+ require_relative "easytest/matcher/true"
16
27
 
17
28
  module Easytest
18
- def self.run
19
- passed_count = 0
20
- failed_count = 0
21
- file_count = 0
22
-
23
- cases_by_file = cases.group_by { |c| c.file }
24
- cases_by_file.each do |file, cases|
25
- file_count += 1
26
- reports = []
27
-
28
- cases.each do |c|
29
- passed = c.run
30
-
31
- if passed
32
- passed_count += 1
33
- else
34
- failed_count += 1
35
- reports << c.report.gsub(/^/, " ")
36
- end
37
- end
38
-
39
- link = Utils.terminal_hyperlink(file)
40
- if reports.empty?
41
- puts "#{Rainbow(" PASS ").bright.bg(:green)} #{link}"
42
- else
43
- puts "#{Rainbow(" FAIL ").bright.bg(:red)} #{link}"
44
- reports.each { |report| puts report ; puts "" }
45
- end
46
- end
47
-
48
- [passed_count, failed_count, file_count]
49
- end
50
-
51
- def self.cases
52
- @cases ||= []
29
+ def self.start
30
+ @runner = Runner.new
53
31
  end
54
32
 
55
33
  def self.add_case(new_case)
56
- cases << new_case
34
+ @runner.cases << new_case
57
35
  end
58
- end
59
-
60
- start_time = Time.now
61
36
 
62
- at_exit do
63
- passed_count, failed_count, file_count = Easytest.run
64
- total_count = passed_count + failed_count
65
-
66
- time = Time.now - start_time
67
-
68
- if total_count == 0
69
- puts Rainbow("A test suite should have at least one test case!").red.bright
70
- puts ""
71
- puts "Please put `test/**/*_test.rb` files or specify valid patterns to the `easytest` command."
72
- else
73
- summary = ""
74
- if failed_count == 0
75
- summary << Rainbow("#{passed_count} passed").green.bright
76
- else
77
- summary << Rainbow("#{failed_count} failed").red.bright
78
- summary << ", #{Rainbow("#{passed_count} passed").green.bright}"
79
- end
80
- summary << ", #{total_count} total #{Rainbow("(#{file_count} files)").dimgray}"
37
+ def self.run
38
+ @runner.run
39
+ end
81
40
 
82
- puts ""
83
- puts " #{Rainbow('Tests:').bright} #{summary}"
84
- puts " #{Rainbow('Time:').bright} #{time.round(5)} seconds"
41
+ def self.test_files_location
42
+ "test/**/*_test.rb"
85
43
  end
86
44
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: easytest
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Masafumi Koba
@@ -38,14 +38,23 @@ files:
38
38
  - exe/easytest
39
39
  - lib/easytest.rb
40
40
  - lib/easytest/case.rb
41
+ - lib/easytest/cli.rb
41
42
  - lib/easytest/dsl.rb
42
43
  - lib/easytest/errors.rb
43
44
  - lib/easytest/expectation.rb
44
45
  - lib/easytest/matcher/base.rb
45
46
  - lib/easytest/matcher/be.rb
46
- - lib/easytest/matcher/be_nil.rb
47
+ - lib/easytest/matcher/be_a.rb
47
48
  - lib/easytest/matcher/equal.rb
49
+ - lib/easytest/matcher/false.rb
50
+ - lib/easytest/matcher/instance_of.rb
51
+ - lib/easytest/matcher/kind_of.rb
52
+ - lib/easytest/matcher/nil.rb
53
+ - lib/easytest/matcher/not_raise.rb
54
+ - lib/easytest/matcher/raise.rb
55
+ - lib/easytest/matcher/true.rb
48
56
  - lib/easytest/reporter.rb
57
+ - lib/easytest/runner.rb
49
58
  - lib/easytest/utils.rb
50
59
  - lib/easytest/version.rb
51
60
  homepage: https://github.com/ybiquitous/easytest
@@ -1,15 +0,0 @@
1
- module Easytest
2
- module Matcher
3
- class BeNil < Base
4
- def match?
5
- actual.nil?
6
- end
7
-
8
- def match!
9
- unless match?
10
- raise UnmatchedError.new(message: "should be `nil`", actual: actual, expected: nil)
11
- end
12
- end
13
- end
14
- end
15
- end