easytest 0.1.1 → 0.2.0

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