test_tube 1.0.0 → 1.1.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 +4 -4
- data/README.md +131 -15
- data/lib/test_tube.rb +18 -2
- data/lib/test_tube/base.rb +18 -3
- data/lib/test_tube/invoker.rb +2 -2
- data/lib/test_tube/passer.rb +2 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f1c0e61708e5bd5a64d8c57b429c1dcdb74c35003fb2717aeb09777c10b58ed4
|
4
|
+
data.tar.gz: 99e9ee9069e4b5a3124fb42566eaf9bed7cce5328335ba8ef0abc96b285f9715
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3e30d9ccb6fc2186e61d6f3278da6db17568fe8144fe3008af9a1523a579eeb345246e4f5bc6675fe9d4ba3090890ef400c535f69f0fd3ba0f909f8511d17545
|
7
|
+
data.tar.gz: c58676b8830b438bfde6fc83a12cf8c110e0049da7b7b63385742628e304bce385bfcbed2303d21abb26bc7b76c647394caf0d6a41679bd5e3939e762b0aa83a
|
data/README.md
CHANGED
@@ -6,6 +6,8 @@
|
|
6
6
|
|
7
7
|
> A test tube to conduct software experiments 🧪
|
8
8
|
|
9
|
+

|
10
|
+
|
9
11
|
## Installation
|
10
12
|
|
11
13
|
Add this line to your application's Gemfile:
|
@@ -28,8 +30,14 @@ gem install test_tube
|
|
28
30
|
|
29
31
|
## Usage
|
30
32
|
|
33
|
+
To make __TestTube__ available:
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
require "test_tube"
|
37
|
+
```
|
38
|
+
|
31
39
|
Assuming we'd like to experiment on the answer to the Ultimate Question of Life,
|
32
|
-
the Universe, and Everything with the following
|
40
|
+
the Universe, and Everything with the following _matcher_:
|
33
41
|
|
34
42
|
```ruby
|
35
43
|
class BeTheAnswer
|
@@ -39,35 +47,143 @@ class BeTheAnswer
|
|
39
47
|
end
|
40
48
|
```
|
41
49
|
|
42
|
-
|
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:
|
43
55
|
|
44
56
|
```ruby
|
45
|
-
|
46
|
-
|
57
|
+
block_of_code = -> { "101010".to_i(2) }
|
58
|
+
|
59
|
+
experiment = TestTube.invoke(
|
60
|
+
block_of_code,
|
47
61
|
isolation: false,
|
48
62
|
matcher: BeTheAnswer.new,
|
49
63
|
negate: false
|
50
64
|
)
|
51
|
-
# => #<TestTube::Content:0x00007fb3b328b248 @actual=42, @got=true, @error=nil>
|
52
65
|
|
53
|
-
|
54
|
-
|
55
|
-
|
66
|
+
experiment.actual # => 42
|
67
|
+
experiment.error # => nil
|
68
|
+
experiment.got # => true
|
56
69
|
```
|
57
70
|
|
58
|
-
An alternative would be to
|
71
|
+
An alternative would be to `pass` directly the actual value as a parameter:
|
59
72
|
|
60
73
|
```ruby
|
61
|
-
|
62
|
-
|
74
|
+
actual_value = "101010".to_i(2)
|
75
|
+
|
76
|
+
experiment = TestTube.pass(
|
77
|
+
actual_value,
|
63
78
|
matcher: BeTheAnswer.new,
|
64
79
|
negate: false
|
65
80
|
)
|
66
|
-
# => #<TestTube::Passer:0x00007f85c229c2d8 @actual=42, @got=true>
|
67
81
|
|
68
|
-
|
69
|
-
|
70
|
-
|
82
|
+
experiment.actual # => 42
|
83
|
+
experiment.error # => nil
|
84
|
+
experiment.got # => true
|
85
|
+
```
|
86
|
+
|
87
|
+
### __Matchi__ matchers
|
88
|
+
To facilitate the addition of matchers, a collection is available via the
|
89
|
+
[__Matchi__ project](https://github.com/fixrb/matchi/).
|
90
|
+
|
91
|
+
Let's use a built-in __Matchi__ matcher:
|
92
|
+
|
93
|
+
```sh
|
94
|
+
gem install matchi
|
95
|
+
```
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
require "matchi"
|
99
|
+
```
|
100
|
+
|
101
|
+
An example of successful experience:
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
experiment = TestTube.invoke(
|
105
|
+
-> { "foo".blank? },
|
106
|
+
isolation: false,
|
107
|
+
matcher: Matchi::Matcher::RaiseException.new(NoMethodError),
|
108
|
+
negate: false
|
109
|
+
)
|
110
|
+
|
111
|
+
experiment.actual # => #<NoMethodError: undefined method `blank?' for "foo":String>
|
112
|
+
experiment.error # => nil
|
113
|
+
experiment.got # => true
|
114
|
+
```
|
115
|
+
|
116
|
+
Another example of an experiment that fails:
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
experiment = TestTube.invoke(
|
120
|
+
-> { 0.1 + 0.2 },
|
121
|
+
isolation: false,
|
122
|
+
matcher: Matchi::Matcher::Equal.new(0.3),
|
123
|
+
negate: false
|
124
|
+
)
|
125
|
+
|
126
|
+
experiment.actual # => 0.30000000000000004
|
127
|
+
experiment.error # => nil
|
128
|
+
experiment.got # => false
|
129
|
+
```
|
130
|
+
|
131
|
+
Finally, an experiment which causes an error:
|
132
|
+
|
133
|
+
```ruby
|
134
|
+
experiment = TestTube.invoke(
|
135
|
+
-> { BOOM },
|
136
|
+
isolation: false,
|
137
|
+
matcher: Matchi::Matcher::Match.new(/^foo$/),
|
138
|
+
negate: false
|
139
|
+
)
|
140
|
+
|
141
|
+
experiment.actual # => nil
|
142
|
+
experiment.error # => #<NameError: uninitialized constant BOOM>
|
143
|
+
experiment.got # => nil
|
144
|
+
```
|
145
|
+
|
146
|
+
### Code isolation
|
147
|
+
|
148
|
+
When experimenting tests, side-effects may occur. Because they may or may not be
|
149
|
+
desired, an `isolation` option is available.
|
150
|
+
|
151
|
+
Let's for instance consider this block of code:
|
152
|
+
|
153
|
+
```ruby
|
154
|
+
greeting = "Hello, world!"
|
155
|
+
block_of_code = -> { greeting.gsub!("world", "Alice") }
|
156
|
+
```
|
157
|
+
|
158
|
+
By setting the `isolation` option to `true`, we can experiment while avoiding
|
159
|
+
side effects:
|
160
|
+
|
161
|
+
```ruby
|
162
|
+
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>
|
170
|
+
|
171
|
+
greeting # => "Hello, world!"
|
172
|
+
```
|
173
|
+
|
174
|
+
Otherwise, we can experiment without any code isolation:
|
175
|
+
|
176
|
+
```ruby
|
177
|
+
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>
|
185
|
+
|
186
|
+
greeting # => "Hello, Alice!"
|
71
187
|
```
|
72
188
|
|
73
189
|
## Contact
|
data/lib/test_tube.rb
CHANGED
@@ -11,7 +11,15 @@ module TestTube
|
|
11
11
|
# @param negate [Boolean] Invert the matcher or not.
|
12
12
|
#
|
13
13
|
# @example
|
14
|
-
#
|
14
|
+
# require "test_tube"
|
15
|
+
#
|
16
|
+
# class BeTheAnswer
|
17
|
+
# def matches?
|
18
|
+
# 42.equal?(yield)
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# TestTube.invoke(
|
15
23
|
# -> { "101010".to_i(2) },
|
16
24
|
# isolation: false,
|
17
25
|
# matcher: BeTheAnswer.new,
|
@@ -28,7 +36,15 @@ module TestTube
|
|
28
36
|
# @param negate [Boolean] Invert the matcher or not.
|
29
37
|
#
|
30
38
|
# @example
|
31
|
-
#
|
39
|
+
# require "test_tube"
|
40
|
+
#
|
41
|
+
# class BeTheAnswer
|
42
|
+
# def matches?
|
43
|
+
# 42.equal?(yield)
|
44
|
+
# end
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# TestTube.pass(
|
32
48
|
# "101010".to_i(2),
|
33
49
|
# matcher: BeTheAnswer.new,
|
34
50
|
# negate: false
|
data/lib/test_tube/base.rb
CHANGED
@@ -1,15 +1,30 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module TestTube
|
4
|
-
#
|
4
|
+
# Abstract class representing the state of an experiment.
|
5
5
|
class Base
|
6
|
+
# Expectation's actual value.
|
7
|
+
#
|
6
8
|
# @return [#object_id] The actual value.
|
7
9
|
attr_reader :actual
|
8
10
|
|
9
|
-
#
|
11
|
+
# Expectation's raised error.
|
12
|
+
#
|
13
|
+
# @return [Exception, nil] The raised error.
|
10
14
|
attr_reader :error
|
11
15
|
|
12
|
-
#
|
16
|
+
# Expectation's returned boolean value.
|
17
|
+
#
|
18
|
+
# @return [Boolean, nil] The returned boolean value.
|
13
19
|
attr_reader :got
|
20
|
+
|
21
|
+
# A string containing a human-readable representation of the experiment.
|
22
|
+
#
|
23
|
+
# @return [String] The human-readable representation of the experiment.
|
24
|
+
def inspect
|
25
|
+
"<TestTube actual=#{actual.inspect} error=#{error.inspect} got=#{got.inspect}>"
|
26
|
+
end
|
27
|
+
|
28
|
+
alias to_s inspect
|
14
29
|
end
|
15
30
|
end
|
data/lib/test_tube/invoker.rb
CHANGED
@@ -5,9 +5,9 @@ require "defi"
|
|
5
5
|
require_relative "base"
|
6
6
|
|
7
7
|
module TestTube
|
8
|
-
#
|
8
|
+
# Evaluate an actual value invoking it with #call method.
|
9
9
|
class Invoker < Base
|
10
|
-
#
|
10
|
+
# Class initializer.
|
11
11
|
#
|
12
12
|
# rubocop:disable Lint/RescueException, Metrics/MethodLength
|
13
13
|
#
|
data/lib/test_tube/passer.rb
CHANGED
@@ -3,9 +3,9 @@
|
|
3
3
|
require_relative "base"
|
4
4
|
|
5
5
|
module TestTube
|
6
|
-
#
|
6
|
+
# Evaluate an actual value passed in parameter.
|
7
7
|
class Passer < Base
|
8
|
-
#
|
8
|
+
# Class initializer.
|
9
9
|
#
|
10
10
|
# @param input [#object_id] An actual value to test.
|
11
11
|
# @param matcher [#matches?] A matcher.
|
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.
|
4
|
+
version: 1.1.0
|
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-
|
11
|
+
date: 2021-06-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: defi
|