test_tube 1.1.0 → 2.1.2

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: f1c0e61708e5bd5a64d8c57b429c1dcdb74c35003fb2717aeb09777c10b58ed4
4
- data.tar.gz: 99e9ee9069e4b5a3124fb42566eaf9bed7cce5328335ba8ef0abc96b285f9715
3
+ metadata.gz: cba8c30ae585d6ac402dffd73a643e1f56b5e3341ae02c7299f1b9a4bb34b38f
4
+ data.tar.gz: 1b4d9ca54c4eb275982bbee603c16a82639a988920a61f8e9b2029a8b4ee6471
5
5
  SHA512:
6
- metadata.gz: 3e30d9ccb6fc2186e61d6f3278da6db17568fe8144fe3008af9a1523a579eeb345246e4f5bc6675fe9d4ba3090890ef400c535f69f0fd3ba0f909f8511d17545
7
- data.tar.gz: c58676b8830b438bfde6fc83a12cf8c110e0049da7b7b63385742628e304bce385bfcbed2303d21abb26bc7b76c647394caf0d6a41679bd5e3939e762b0aa83a
6
+ metadata.gz: 4332f949beca9c924e535917bca0df8538c385daeebb60bbced8f4b3868f05112f86c140147f01b34d4c6d3d68a5c0deea4879769d7af796d2d1a87af6a6bcf5
7
+ data.tar.gz: 15dbac8b721a1d11849138783f009462bbdee16b899713db691432d33285bf997da4068efaf61fee874e12adb47dcc66d984d036a8205124b111139f71f05945
data/README.md CHANGED
@@ -1,8 +1,10 @@
1
1
  # Test Tube
2
2
 
3
- [![Build Status](https://api.travis-ci.org/fixrb/test_tube.svg?branch=main)](https://travis-ci.org/fixrb/test_tube)
4
- [![Gem Version](https://badge.fury.io/rb/test_tube.svg)](https://rubygems.org/gems/test_tube)
5
- [![Documentation](https://img.shields.io/:yard-docs-38c800.svg)](https://rubydoc.info/gems/test_tube)
3
+ [![Version](https://img.shields.io/github/v/tag/fixrb/test_tube?label=Version&logo=github)](https://github.com/fixrb/test_tube/releases)
4
+ [![Yard documentation](https://img.shields.io/badge/Yard-documentation-blue.svg?logo=github)](https://rubydoc.info/github/fixrb/test_tube/main)
5
+ [![CI](https://github.com/fixrb/test_tube/workflows/CI/badge.svg?branch=main)](https://github.com/fixrb/test_tube/actions?query=workflow%3Aci+branch%3Amain)
6
+ [![RuboCop](https://github.com/fixrb/test_tube/workflows/RuboCop/badge.svg?branch=main)](https://github.com/fixrb/test_tube/actions?query=workflow%3Arubocop+branch%3Amain)
7
+ [![License](https://img.shields.io/github/license/fixrb/test_tube?label=License&logo=github)](https://github.com/fixrb/test_tube/raw/main/LICENSE.md)
6
8
 
7
9
  > A test tube to conduct software experiments 🧪
8
10
 
@@ -37,7 +39,7 @@ require "test_tube"
37
39
  ```
38
40
 
39
41
  Assuming we'd like to experiment on the answer to the Ultimate Question of Life,
40
- the Universe, and Everything with the following _matcher_:
42
+ the Universe, and Everything with the following matcher:
41
43
 
42
44
  ```ruby
43
45
  class BeTheAnswer
@@ -47,21 +49,13 @@ class BeTheAnswer
47
49
  end
48
50
  ```
49
51
 
50
- A _matcher_ is an object that responds to the `matches?` method with a block
51
- parameter representing the _actual value_ to be compared.
52
-
53
- Back to our Ruby experiments, one possibility would be to `invoke` a whole block
54
- of code:
52
+ One possibility would be to `invoke` a whole block of code:
55
53
 
56
54
  ```ruby
57
55
  block_of_code = -> { "101010".to_i(2) }
58
56
 
59
- experiment = TestTube.invoke(
60
- block_of_code,
61
- isolation: false,
62
- matcher: BeTheAnswer.new,
63
- negate: false
64
- )
57
+ experiment = TestTube.invoke(isolate: false, matcher: BeTheAnswer.new, negate: false, &block_of_code)
58
+ # => <TestTube actual=42 error=nil got=true>
65
59
 
66
60
  experiment.actual # => 42
67
61
  experiment.error # => nil
@@ -73,11 +67,8 @@ An alternative would be to `pass` directly the actual value as a parameter:
73
67
  ```ruby
74
68
  actual_value = "101010".to_i(2)
75
69
 
76
- experiment = TestTube.pass(
77
- actual_value,
78
- matcher: BeTheAnswer.new,
79
- negate: false
80
- )
70
+ experiment = TestTube.pass(actual_value, matcher: BeTheAnswer.new, negate: false)
71
+ # => <TestTube actual=42 error=nil got=true>
81
72
 
82
73
  experiment.actual # => 42
83
74
  experiment.error # => nil
@@ -85,6 +76,7 @@ experiment.got # => true
85
76
  ```
86
77
 
87
78
  ### __Matchi__ matchers
79
+
88
80
  To facilitate the addition of matchers, a collection is available via the
89
81
  [__Matchi__ project](https://github.com/fixrb/matchi/).
90
82
 
@@ -102,11 +94,11 @@ An example of successful experience:
102
94
 
103
95
  ```ruby
104
96
  experiment = TestTube.invoke(
105
- -> { "foo".blank? },
106
- isolation: false,
107
- matcher: Matchi::Matcher::RaiseException.new(NoMethodError),
108
- negate: false
109
- )
97
+ isolate: false,
98
+ matcher: Matchi::RaiseException.new(:NoMethodError),
99
+ negate: false
100
+ ) { "foo".blank? }
101
+ # => <TestTube actual=#<NoMethodError: undefined method `blank?' for "foo":String> error=nil got=true>
110
102
 
111
103
  experiment.actual # => #<NoMethodError: undefined method `blank?' for "foo":String>
112
104
  experiment.error # => nil
@@ -117,11 +109,11 @@ Another example of an experiment that fails:
117
109
 
118
110
  ```ruby
119
111
  experiment = TestTube.invoke(
120
- -> { 0.1 + 0.2 },
121
- isolation: false,
122
- matcher: Matchi::Matcher::Equal.new(0.3),
123
- negate: false
124
- )
112
+ isolate: false,
113
+ matcher: Matchi::Be.new(0.3),
114
+ negate: false,
115
+ &-> { 0.1 + 0.2 }
116
+ ) # => <TestTube actual=0.30000000000000004 error=nil got=false>
125
117
 
126
118
  experiment.actual # => 0.30000000000000004
127
119
  experiment.error # => nil
@@ -132,11 +124,11 @@ Finally, an experiment which causes an error:
132
124
 
133
125
  ```ruby
134
126
  experiment = TestTube.invoke(
135
- -> { BOOM },
136
- isolation: false,
137
- matcher: Matchi::Matcher::Match.new(/^foo$/),
138
- negate: false
139
- )
127
+ isolate: false,
128
+ matcher: Matchi::Match.new(/^foo$/),
129
+ negate: false
130
+ ) { BOOM }
131
+ # => <TestTube actual=nil error=#<NameError: uninitialized constant BOOM> got=nil>
140
132
 
141
133
  experiment.actual # => nil
142
134
  experiment.error # => #<NameError: uninitialized constant BOOM>
@@ -146,27 +138,25 @@ experiment.got # => nil
146
138
  ### Code isolation
147
139
 
148
140
  When experimenting tests, side-effects may occur. Because they may or may not be
149
- desired, an `isolation` option is available.
141
+ desired, an `isolate` option is available.
150
142
 
151
143
  Let's for instance consider this block of code:
152
144
 
153
145
  ```ruby
154
146
  greeting = "Hello, world!"
155
- block_of_code = -> { greeting.gsub!("world", "Alice") }
147
+ block_of_code = -> { greeting.gsub!("world", "Alice") } # => #<Proc:0x00007f87f71b9690 (irb):42 (lambda)>
156
148
  ```
157
149
 
158
- By setting the `isolation` option to `true`, we can experiment while avoiding
150
+ By setting the `isolate` option to `true`, we can experiment while avoiding
159
151
  side effects:
160
152
 
161
153
  ```ruby
162
154
  experiment = TestTube.invoke(
163
- block_of_code,
164
- isolation: true,
165
- matcher: Matchi::Matcher::Eql.new("Hello, Alice!"),
166
- negate: false
167
- )
168
-
169
- experiment.inspect # => <TestTube actual="Hello, Alice!" error=nil got=true>
155
+ isolate: true,
156
+ matcher: Matchi::Eq.new("Hello, Alice!"),
157
+ negate: false,
158
+ &block_of_code
159
+ ) # => <TestTube actual="Hello, Alice!" error=nil got=true>
170
160
 
171
161
  greeting # => "Hello, world!"
172
162
  ```
@@ -175,13 +165,11 @@ Otherwise, we can experiment without any code isolation:
175
165
 
176
166
  ```ruby
177
167
  experiment = TestTube.invoke(
178
- block_of_code,
179
- isolation: false,
180
- matcher: Matchi::Matcher::Eql.new("Hello, Alice!"),
181
- negate: false
182
- )
183
-
184
- experiment.inspect # => <TestTube actual="Hello, Alice!" error=nil got=true>
168
+ isolate: false,
169
+ matcher: Matchi::Eq.new("Hello, Alice!"),
170
+ negate: false,
171
+ &block_of_code
172
+ ) # => <TestTube actual="Hello, Alice!" error=nil got=true>
185
173
 
186
174
  greeting # => "Hello, Alice!"
187
175
  ```
@@ -189,6 +177,8 @@ greeting # => "Hello, Alice!"
189
177
  ## Contact
190
178
 
191
179
  * Source code: https://github.com/fixrb/test_tube
180
+ * Chinese blog post: https://ruby-china.org/topics/41390
181
+ * Japanese blog post: https://qiita.com/cyril/items/36174b619ff1852c80ec
192
182
 
193
183
  ## Versioning
194
184
 
@@ -196,7 +186,7 @@ __Test Tube__ follows [Semantic Versioning 2.0](https://semver.org/).
196
186
 
197
187
  ## License
198
188
 
199
- The [gem](https://rubygems.org/gems/test_tube) is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
189
+ The [gem](https://rubygems.org/gems/test_tube) is available as open source under the terms of the [MIT License](https://github.com/fixrb/test_tube/raw/main/LICENSE.md).
200
190
 
201
191
  ***
202
192
 
data/lib/test_tube.rb CHANGED
@@ -4,11 +4,13 @@ require_relative File.join("test_tube", "invoker")
4
4
  require_relative File.join("test_tube", "passer")
5
5
 
6
6
  # Namespace for the TestTube library.
7
+ #
8
+ # @api public
7
9
  module TestTube
8
- # @param input [#call] The callable object to test.
9
- # @param isolation [Boolean] Compute in isolation or not.
10
- # @param matcher [#matches?] A matcher.
11
- # @param negate [Boolean] Invert the matcher or not.
10
+ # @param isolate [Boolean] Compute in a subprocess.
11
+ # @param matcher [#matches?] A matcher.
12
+ # @param negate [Boolean] Invert the matcher or not.
13
+ # @param input [Proc] The callable object to test.
12
14
  #
13
15
  # @example
14
16
  # require "test_tube"
@@ -19,21 +21,18 @@ module TestTube
19
21
  # end
20
22
  # end
21
23
  #
22
- # TestTube.invoke(
23
- # -> { "101010".to_i(2) },
24
- # isolation: false,
25
- # matcher: BeTheAnswer.new,
26
- # negate: false
27
- # )
24
+ # TestTube.invoke(isolate: false, matcher: BeTheAnswer.new, negate: false) do
25
+ # "101010".to_i(2)
26
+ # end
28
27
  #
29
28
  # @return [Invoker] A software experiment.
30
- def self.invoke(input, isolation:, matcher:, negate:)
31
- Invoker.new(input, isolation: isolation, matcher: matcher, negate: negate)
29
+ def self.invoke(isolate:, matcher:, negate:, &input)
30
+ Invoker.new(isolate: isolate, matcher: matcher, negate: negate, &input)
32
31
  end
33
32
 
34
- # @param input [#object_id] The callable object to test.
35
- # @param matcher [#matches?] A matcher.
36
- # @param negate [Boolean] Invert the matcher or not.
33
+ # @param input [#object_id] The actual value to test.
34
+ # @param matcher [#matches?] A matcher.
35
+ # @param negate [Boolean] Invert the matcher or not.
37
36
  #
38
37
  # @example
39
38
  # require "test_tube"
@@ -44,10 +43,9 @@ module TestTube
44
43
  # end
45
44
  # end
46
45
  #
47
- # TestTube.pass(
48
- # "101010".to_i(2),
49
- # matcher: BeTheAnswer.new,
50
- # negate: false
46
+ # TestTube.pass("101010".to_i(2),
47
+ # matcher: BeTheAnswer.new,
48
+ # negate: false
51
49
  # )
52
50
  #
53
51
  # @return [Passer] A software experiment.
@@ -2,25 +2,35 @@
2
2
 
3
3
  module TestTube
4
4
  # Abstract class representing the state of an experiment.
5
- class Base
5
+ #
6
+ # @api private
7
+ class Base < ::BasicObject
6
8
  # Expectation's actual value.
7
9
  #
8
10
  # @return [#object_id] The actual value.
11
+ #
12
+ # @api public
9
13
  attr_reader :actual
10
14
 
11
15
  # Expectation's raised error.
12
16
  #
13
17
  # @return [Exception, nil] The raised error.
18
+ #
19
+ # @api public
14
20
  attr_reader :error
15
21
 
16
22
  # Expectation's returned boolean value.
17
23
  #
18
24
  # @return [Boolean, nil] The returned boolean value.
25
+ #
26
+ # @api public
19
27
  attr_reader :got
20
28
 
21
29
  # A string containing a human-readable representation of the experiment.
22
30
  #
23
31
  # @return [String] The human-readable representation of the experiment.
32
+ #
33
+ # @api public
24
34
  def inspect
25
35
  "<TestTube actual=#{actual.inspect} error=#{error.inspect} got=#{got.inspect}>"
26
36
  end
@@ -6,28 +6,28 @@ require_relative "base"
6
6
 
7
7
  module TestTube
8
8
  # Evaluate an actual value invoking it with #call method.
9
+ #
10
+ # @api private
9
11
  class Invoker < Base
10
12
  # Class initializer.
11
13
  #
12
14
  # rubocop:disable Lint/RescueException, Metrics/MethodLength
13
15
  #
14
- # @param input [#call] The callable object to test.
15
- # @param isolation [Boolean] Compute in isolation or not.
16
- # @param matcher [#matches?] A matcher.
17
- # @param negate [Boolean] Invert the matcher or not.
18
- def initialize(input, isolation:, matcher:, negate:)
16
+ # @param isolate [Boolean] Compute in a subprocess.
17
+ # @param matcher [#matches?] A matcher.
18
+ # @param negate [Boolean] Invert the matcher or not.
19
+ # @param input [Proc] The callable object to test.
20
+ def initialize(isolate:, matcher:, negate:, &input)
19
21
  super()
20
22
 
21
- @got = negate ^ matcher.matches? do
22
- value = if isolation
23
- send_call.to!(input)
24
- else
25
- send_call.to(input)
26
- end
23
+ value = if isolate
24
+ send_call.to!(input)
25
+ else
26
+ send_call.to(input)
27
+ end
27
28
 
28
- @actual = value.object
29
- value.call
30
- end
29
+ @actual = value.object
30
+ @got = negate ^ matcher.matches? { value.call }
31
31
  rescue ::Exception => e
32
32
  @actual = nil
33
33
  @error = e
@@ -4,10 +4,12 @@ require_relative "base"
4
4
 
5
5
  module TestTube
6
6
  # Evaluate an actual value passed in parameter.
7
+ #
8
+ # @api private
7
9
  class Passer < Base
8
10
  # Class initializer.
9
11
  #
10
- # @param input [#object_id] An actual value to test.
12
+ # @param input [#object_id] The actual value to test.
11
13
  # @param matcher [#matches?] A matcher.
12
14
  # @param negate [Boolean] Invert the matcher or not.
13
15
  def initialize(input, matcher:, negate:)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: test_tube
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 2.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cyril Kato
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-20 00:00:00.000000000 Z
11
+ date: 2021-08-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: defi
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 2.0.5
19
+ version: 2.0.6
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 2.0.5
26
+ version: 2.0.6
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: brutal
29
29
  requirement: !ruby/object:Gem::Requirement