test_tube 1.1.0 → 2.1.2

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: 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