easytest 0.1.2 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 42389cdde1ebd51de0f636f3c5ec0e67ffe842ff91029f71617795799841d47b
4
- data.tar.gz: 559dadc55a86bfc6da11234b05f1fd9a5477311379f94ea41565731516271405
3
+ metadata.gz: 9c1fb5f44f3dd5d049cd6775120712a614f7016a20c7f87d771043642ceb7bb6
4
+ data.tar.gz: '04591bad578ad4351de07ab07467378663cea9e0de2d40930aea5d4a3d132bac'
5
5
  SHA512:
6
- metadata.gz: e4d9cc9b6bc5a76ecccf5f98d076e123eeddabe7a16c42a093050e154c3c32565a80c99301ed615ab0be95e2b7b7e0773088d3d5dc621ddc2fda3f69e28ed184
7
- data.tar.gz: 8a8612f4baf5bc8bc026ecb20fbbe906ad32044f22c9232137dc68b5bb32940d634aed10703d6455f339518bba18c8a433bee66d2ed30afbcff2c93cbe03ec57
6
+ metadata.gz: 718b38f0a0fc35339bdaae214baf115de1ee8d4a94bb20d157f102af5caf08a763580e2946976504443e3b45211fc5d8776668b353c9a9c07e5ed9abddca03e3
7
+ data.tar.gz: 4784ee059e028d9d8a7c6ed23d864182e280ced00d56e502b87d31b032212bde623b50b66fed087583f0ee24eda939900915d8d25fe9081bdf33b020930b7423
data/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## 0.3.0
6
+
7
+ - Add RBS support.
8
+ - Add CLI help.
9
+ - Fix CLI to aware absolute paths.
10
+
11
+ ## 0.2.0
12
+
13
+ - Add many matchers.
14
+
5
15
  ## 0.1.2
6
16
 
7
17
  - Fix the `easytest` command exit code.
data/README.md CHANGED
@@ -1,10 +1,11 @@
1
1
  [![Gem Version](https://badge.fury.io/rb/easytest.svg)](https://badge.fury.io/rb/easytest)
2
+ [![CI](https://github.com/ybiquitous/easytest/actions/workflows/ci.yml/badge.svg)](https://github.com/ybiquitous/easytest/actions/workflows/ci.yml)
2
3
 
3
4
  # Easytest
4
5
 
5
6
  > makes you write tests easily
6
7
 
7
- Easytest is a tiny testing framework with a familiar DSL.
8
+ Easytest is a tiny testing framework for Ruby with a familiar DSL.
8
9
 
9
10
  ## Installation
10
11
 
@@ -39,14 +40,14 @@ end
39
40
  Then, run `easytest`:
40
41
 
41
42
  ```console
42
- $ bundle exec easytest
43
+ $ easytest
43
44
  FAIL test/addition_test.rb
44
45
  ● addition (should equal)
45
46
 
46
47
  Expected: 2
47
48
  Received: 3
48
49
 
49
- at test/addition_test.rb:6:in `block in <top (required)>'
50
+ # test/addition_test.rb:6:in `block in <top (required)>'
50
51
 
51
52
 
52
53
  Tests: 1 failed, 0 passed, 1 total (1 files)
@@ -63,11 +64,13 @@ Oops. Let's fix the failure:
63
64
  Then, run it again:
64
65
 
65
66
  ```console
66
- $ bundle exec easytest
67
+ $ easytest
67
68
  PASS test/addition_test.rb
68
69
 
69
70
  Tests: 1 passed, 1 total (1 files)
70
71
  Time: 0.00077 seconds
71
72
  ```
72
73
 
73
- The test passes! 🎉
74
+ The test now passes! 🎉
75
+
76
+ For more, try `easytest --help`.
data/exe/easytest CHANGED
@@ -3,6 +3,6 @@
3
3
  $LOAD_PATH << File.expand_path("../lib", __dir__)
4
4
  $LOAD_PATH << File.join(Dir.pwd, "test")
5
5
 
6
- require "easytest"
6
+ require "easytest/cli"
7
7
 
8
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
data/lib/easytest/cli.rb CHANGED
@@ -1,7 +1,23 @@
1
+ require "easytest"
2
+ require "optparse"
3
+
1
4
  module Easytest
2
5
  class CLI
3
6
  SUCCESS = 0
4
7
  FAILURE = 1
8
+ FATAL = 2
9
+
10
+ def run
11
+ exit_code = parse_options
12
+ return exit_code if exit_code
13
+
14
+ Easytest.start
15
+ setup
16
+ successful = Easytest.run
17
+ successful ? SUCCESS : FAILURE
18
+ end
19
+
20
+ private
5
21
 
6
22
  attr_reader :argv
7
23
  attr_reader :start_time
@@ -11,18 +27,72 @@ module Easytest
11
27
  @start_time = Time.now
12
28
  end
13
29
 
14
- def run
15
- Easytest.start
16
- setup
17
- successful = Easytest.run
18
- successful ? SUCCESS : FAILURE
30
+ def parse_options
31
+ options = {}
32
+
33
+ parser = OptionParser.new do |p|
34
+ p.program_name = "easytest"
35
+ p.version = "#{p.program_name} #{Easytest::VERSION}"
36
+
37
+ p.on "--help" do
38
+ options[:help] = true
39
+ end
40
+
41
+ p.on "--version" do
42
+ options[:version] = true
43
+ end
44
+ end
45
+
46
+ begin
47
+ parser.parse!(argv)
48
+
49
+ if options[:help]
50
+ puts help(parser)
51
+ return SUCCESS
52
+ end
53
+
54
+ if options[:version]
55
+ puts parser.version
56
+ return SUCCESS
57
+ end
58
+
59
+ nil
60
+ rescue OptionParser::ParseError => error
61
+ $stderr.puts Rainbow(error.message).red
62
+ FATAL
63
+ end
19
64
  end
20
65
 
21
- private
66
+ def help(parser)
67
+ <<~MSG
68
+ #{Rainbow("USAGE").bright}
69
+ #{parser.program_name} [options] [...<file, directory, or pattern>]
70
+
71
+ #{Rainbow("OPTIONS").bright}
72
+ --help Show help
73
+ --version Show version
74
+
75
+ #{Rainbow("EXAMPLES").bright}
76
+ # Run all tests (test/**/*_test.rb)
77
+ $ easytest
78
+
79
+ # Run only test files
80
+ $ easytest test/example_test.rb
81
+
82
+ # Run only test files in specified directories
83
+ $ easytest test/example
84
+
85
+ # Run only test files that matches specified patterns
86
+ $ easytest example
87
+ MSG
88
+ end
22
89
 
23
90
  def setup
24
91
  Dir.glob(Easytest.test_files_location)
25
- .filter { |file| argv.empty? || argv.any? { |pattern| file.include?(pattern) } }
92
+ .map { |file| File.absolute_path(file) }
93
+ .filter do |file|
94
+ argv.empty? || argv.any? { |pattern| file.include?(pattern) }
95
+ end
26
96
  .each { |test_file| load test_file }
27
97
  end
28
98
  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,41 +1,67 @@
1
1
  module Easytest
2
2
  class Expectation
3
- attr_reader :actual
4
- attr_reader :block
5
-
6
- def initialize(actual, &block)
7
- @actual = actual
8
- @block = block
3
+ def not
4
+ self.class.new(actual, negate: true, &block)
9
5
  end
10
6
 
11
7
  def to_eq(expected)
12
- Matcher::Equal.new(actual: eval_actual, expected: expected).match!
8
+ Matcher::Equal.new(actual: actual, expected: expected, negate: negate).match!
13
9
  end
10
+ alias to_equal to_eq
14
11
 
15
12
  def to_be(expected)
16
- Matcher::Be.new(actual: eval_actual, expected: expected).match!
13
+ Matcher::Be.new(actual: actual, expected: expected, negate: negate).match!
17
14
  end
18
15
 
19
16
  def to_be_nil
20
- Matcher::BeNil.new(actual: eval_actual).match!
17
+ Matcher::Nil.new(actual: actual, negate: negate).match!
18
+ end
19
+
20
+ def to_be_true
21
+ Matcher::True.new(actual: actual, negate: negate).match!
22
+ end
23
+
24
+ def to_be_false
25
+ Matcher::False.new(actual: actual, negate: negate).match!
26
+ end
27
+
28
+ def to_be_a(expected)
29
+ Matcher::BeA.new(actual: actual, expected: expected, negate: negate).match!
30
+ end
31
+
32
+ def to_be_kind_of(expected)
33
+ Matcher::KindOf.new(actual: actual, expected: expected, negate: negate).match!
34
+ end
35
+
36
+ def to_be_instance_of(expected)
37
+ Matcher::InstanceOf.new(actual: actual, expected: expected, negate: negate).match!
21
38
  end
22
39
 
23
- def to_raise(exception_class)
40
+ def to_raise(expected)
41
+ raise FatalError, "`to_raise` requires a block like `expect { ... }.to_raise`" unless block
42
+ raise FatalError, "`not.to_raise` can cause a false positive, so use `to_not_raise` instead" if negate?
43
+ raise FatalError, "`to_raise` requires a Class, String, or Regexp" unless [Class, String, Regexp].any? { expected.is_a? _1 }
24
44
 
45
+ Matcher::Raise.new(actual: block, expected: expected, negate: negate).match!
25
46
  end
26
47
 
27
- def to_not_raise(exception_class)
48
+ def to_not_raise
49
+ raise FatalError, "`to_not_raise` requires a block like `expect { ... }.to_not_raise`" unless block
28
50
 
51
+ Matcher::NotRaise.new(actual: block).match!
29
52
  end
30
53
 
31
54
  private
32
55
 
33
- def eval_actual
34
- if block
35
- block.call
36
- else
37
- actual
38
- end
56
+ attr_reader :actual
57
+ attr_reader :block
58
+ attr_reader :negate
59
+ alias negate? negate
60
+
61
+ def initialize(actual, negate: false, &block)
62
+ @actual = actual
63
+ @block = block
64
+ @negate = negate
39
65
  end
40
66
  end
41
67
  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,17 +17,22 @@ module Easytest
12
17
  end
13
18
 
14
19
  def match!
15
- unless match?
16
- raise UnmatchedError.new(message: message, actual: actual, expected: expected)
17
- end
20
+ matched = match?
21
+ matched = !matched if negate?
22
+ raise_match_error unless matched
18
23
  end
19
24
 
20
- def expected
25
+ def message
21
26
  raise NotImplementedError
22
27
  end
23
28
 
24
- def message
25
- raise NotImplementedError
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)
26
36
  end
27
37
  end
28
38
  end
@@ -1,19 +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
8
  def message
16
- "should be same"
9
+ "same"
17
10
  end
18
11
  end
19
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,26 +1,13 @@
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
8
  def message
16
- "should equal"
9
+ "equal"
17
10
  end
18
-
19
- # def match!
20
- # unless match?
21
- # raise UnmatchedError.new(message: "should equal", actual: actual, expected: expected)
22
- # end
23
- # end
24
11
  end
25
12
  end
26
13
  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
@@ -1,16 +1,16 @@
1
1
  module Easytest
2
2
  module Matcher
3
- class BeNil < Base
4
- def match?
5
- actual.nil?
3
+ class Nil < Base
4
+ def initialize(actual:, negate:)
5
+ super(actual: actual, expected: nil, negate: negate)
6
6
  end
7
7
 
8
- def expected
9
- nil
8
+ def match?
9
+ actual.nil?
10
10
  end
11
11
 
12
12
  def message
13
- "should be nil"
13
+ "nil"
14
14
  end
15
15
  end
16
16
  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
+ false
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
@@ -39,10 +39,12 @@ module Easytest
39
39
  end
40
40
 
41
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."
42
+ $stderr.puts <<~MSG
43
+ #{Rainbow("Oops. No tests found!").red.bright}
44
+
45
+ #{Rainbow("Put `#{Easytest.test_files_location}` files to include at least one test case.").red}
46
+ #{Rainbow("Or specify a pattern that matches an existing test file.").red}
47
+ MSG
46
48
  false
47
49
  else
48
50
  puts ""
@@ -1,3 +1,3 @@
1
1
  module Easytest
2
- VERSION = "0.1.2"
2
+ VERSION = "0.3.0"
3
3
  end
data/lib/easytest.rb CHANGED
@@ -4,18 +4,26 @@ require "rainbow"
4
4
  require_relative "easytest/version"
5
5
 
6
6
  require_relative "easytest/case"
7
- require_relative "easytest/cli"
8
7
  require_relative "easytest/dsl"
9
8
  require_relative "easytest/errors"
10
9
  require_relative "easytest/expectation"
11
- require_relative "easytest/matcher/base"
12
- require_relative "easytest/matcher/be"
13
- require_relative "easytest/matcher/be_nil"
14
- require_relative "easytest/matcher/equal"
15
10
  require_relative "easytest/reporter"
16
11
  require_relative "easytest/runner"
17
12
  require_relative "easytest/utils"
18
13
 
14
+ # Matcher
15
+ require_relative "easytest/matcher/base"
16
+ require_relative "easytest/matcher/be"
17
+ require_relative "easytest/matcher/be_a"
18
+ require_relative "easytest/matcher/equal"
19
+ require_relative "easytest/matcher/false"
20
+ require_relative "easytest/matcher/instance_of"
21
+ require_relative "easytest/matcher/kind_of"
22
+ require_relative "easytest/matcher/nil"
23
+ require_relative "easytest/matcher/not_raise"
24
+ require_relative "easytest/matcher/raise"
25
+ require_relative "easytest/matcher/true"
26
+
19
27
  module Easytest
20
28
  def self.start
21
29
  @runner = Runner.new
data/sig/easytest.rbs ADDED
@@ -0,0 +1,37 @@
1
+ module Easytest
2
+ VERSION: String
3
+
4
+ module DSL
5
+ end
6
+
7
+ class Expectation
8
+ def not: () -> instance
9
+
10
+ def to_eq: (untyped expected) -> void
11
+ alias to_equal to_eq
12
+
13
+ def to_be: (untyped expected) -> untyped
14
+
15
+ def to_be_nil: () -> void
16
+
17
+ def to_be_a: (Class expected) -> void
18
+ def to_be_kind_of: (Class expected) -> void
19
+ def to_be_instance_of: (Class expected) -> void
20
+
21
+ def to_be_false: () -> void
22
+ def to_be_true: () -> void
23
+
24
+ def to_raise: ((Class | String | Regexp) expected) -> void
25
+ def to_not_raise: () -> void
26
+ end
27
+ end
28
+
29
+ module Kernel
30
+ def expect: (untyped actual) -> Easytest::Expectation
31
+ | { () -> void } -> Easytest::Expectation
32
+ end
33
+
34
+ # polyfill
35
+ class Object
36
+ def using: (Module target) -> void
37
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: easytest
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Masafumi Koba
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-10-05 00:00:00.000000000 Z
11
+ date: 2022-10-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rainbow
@@ -44,12 +44,20 @@ files:
44
44
  - lib/easytest/expectation.rb
45
45
  - lib/easytest/matcher/base.rb
46
46
  - lib/easytest/matcher/be.rb
47
- - lib/easytest/matcher/be_nil.rb
47
+ - lib/easytest/matcher/be_a.rb
48
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
49
56
  - lib/easytest/reporter.rb
50
57
  - lib/easytest/runner.rb
51
58
  - lib/easytest/utils.rb
52
59
  - lib/easytest/version.rb
60
+ - sig/easytest.rbs
53
61
  homepage: https://github.com/ybiquitous/easytest
54
62
  licenses:
55
63
  - MIT