rast 0.18.0 → 0.19.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: 77e02b108231f3d36dd5e490e9a0ab9cf1334ebb53ae6ffce93cbec2c124423b
4
- data.tar.gz: a738c7603f64ec75eff5a13c87fa472d147c1262efb0340d2b500d76fa590826
3
+ metadata.gz: 3f3ab7d817085dca4c5fc3c3fd0956ca6a6f87eadd9d97554f693aa81908f5e8
4
+ data.tar.gz: 5e2f9d234c29e5cfc0ccab251980099e13af8c3355d7ea83ad3d8dc8a97ff4c1
5
5
  SHA512:
6
- metadata.gz: 88fc71ee6789773bd21b743c9e8bafbabc07654fbc2494f65e7b855930f32f686222ffe0ada7ecafeab1a828b9f45f8a9d94debf5ca7c87603edfbf39e10e4b3
7
- data.tar.gz: '01409ef86dc48e884d0a4afd8cfa6113c054a4bd70aa19e8d5ab34d0fac5f50119f4e26924634dea6c8e710ba131fc1cfaf79ae07a884a40a48e8e6be2a84346'
6
+ metadata.gz: 3fe3b7af6cbc40ce1ab44a8120ac66865b262a751a448d382f72a2cc28999ab45761a21a4747b8c362e27e46e06eaf4f34f4cf794fb64951d680621c9b1a1deb
7
+ data.tar.gz: 1e07f58dd3fa04a7e84b6cf25a4b4956847f1975e21973b4b3ecc62d74c9f28b3676fd7b185224a2a65b0f4eee612efaea92999c8e8dc7dda01f312db1f825c8
data/.travis.yml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ language: ruby
3
+ rvm:
4
+ - 2.0.0
5
+
6
+ before_script:
7
+ - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
8
+ - chmod +x ./cc-test-reporter
9
+ - ./cc-test-reporter before-build
10
+
11
+ script:
12
+ - bundle exec rspec
13
+
14
+ after_script:
15
+ - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Change log
2
2
 
3
+ ## Unreleased
4
+
5
+ - Add travis-ci build.
6
+ - Add RubyGems badge
7
+ - Update documentation.
8
+
9
+ ## Released
10
+
11
+
12
+ - 0.19.0 - [feature] boolean in variables definition to automatically infer
13
+ false and true
14
+ [feature] xrast to skip entire test.
15
+ [feature] Add default outcome in yaml-less configuration. Default
16
+ config will take higher precedence than boolean outcomes.
3
17
  - 0.18.0 - [feature] xspec to skip a test
4
18
  - [feature] Added include rule to isolate scenarios.
5
19
  - [feature] Asterisk can be used as token character.
data/Documentation.md ADDED
@@ -0,0 +1,297 @@
1
+ # Documentation
2
+
3
+ ## The YAML File
4
+
5
+ YAML is the preferred because it simplifies the content of the spec file. YAML contains the variables that affect the outcome, and the expected outcome based on rules that involve the said variables.
6
+
7
+ ```yaml
8
+ specs:
9
+ # spec key uniquely identifies a spec. It has to match the ID when the spec
10
+ # block is invoked in the ruby spec file. Usually the method name.
11
+ spec_key:
12
+ description: Spec Description
13
+
14
+ variables:
15
+ param1: [one, two]
16
+
17
+ outcomes: # required (dictionary)
18
+ true: one[0] # sample outcome.
19
+
20
+ default: DEFAULT # optional (scalar) fall off value.
21
+ ```
22
+
23
+ ### Using a subscript when variables clash
24
+
25
+ A subscript may be used in cases where a variable token used multiple times.
26
+ In the example below, the left variable has the subscript of `0`, and the right variable have the subscript of `1`.
27
+
28
+ [logic_checker_spec.yml](./spec/examples/rast/logic_checker_spec.yml)
29
+
30
+ ```yaml
31
+ specs:
32
+ Logical AND:
33
+ variables:
34
+ left: [false, true]
35
+ right: [false, true]
36
+ outcomes: {true: 'true[0] & true[1]'}
37
+
38
+ Logical OR:
39
+ variables:
40
+ left: [false, true]
41
+ right: [false, true]
42
+ outcomes: {true: 'true[0] | true[1]'}
43
+
44
+ Logical XOR:
45
+ variables:
46
+ left: [false, true]
47
+ right: [false, true]
48
+ outcomes: {true: 'false[0] & true[1] | true[0] & false[1]'}
49
+ ```
50
+
51
+ ### Isolating scenarios
52
+
53
+ For troubleshooting purposes, you can use the `include` attribute to focus on one or more scenarios.
54
+
55
+ ```yaml
56
+ ---
57
+ specs:
58
+ '#positive?':
59
+ variables: {number: [-1, 0, 1]}
60
+ outcomes: {true: 1}
61
+ include: 0
62
+ ```
63
+
64
+ Running `rspec -fd` will result to
65
+
66
+ ```
67
+ Positive: #positive?, ONLY: '0'
68
+ [false]=[number: 0]
69
+
70
+ Finished in 0.00292 seconds (files took 0.26024 seconds to load)
71
+ 1 example, 0 failures
72
+ ```
73
+
74
+
75
+ ### Filtering Out Invalid Cases
76
+
77
+ In a prior example `HotelFinder`, some cases are invalid. For example, if an
78
+ aircon is not available, then it makes to sense to check if it is operational
79
+ not. In such case, we should limit the scenarios with `exclude` clause.
80
+
81
+ ```yaml
82
+ # double_example_spec.yml
83
+ ---
84
+ specs:
85
+ '#applicable?':
86
+ variables:
87
+ Air Conditioning: [false, true]
88
+ Security: [false, true]
89
+ Operational: [false, true]
90
+ Security Grade: [basic, advanced, diplomat]
91
+
92
+ # If airconditioning is false, it does not make sense to test if it is
93
+ # operational. We limit the invalid scenarios to at most 1, so that
94
+ # a scenario where airconditioning is false will still be tested.
95
+ exclude: false[0] & false[2] | false[1] & !basic
96
+ outcomes: {PASSED: 'true[0] & true[1] & true[2] & diplomat'}
97
+ else: INADEQUATE
98
+ ```
99
+
100
+ ### Using a default outcome
101
+
102
+ Defining the rules to outcomes can be the most time consuming part of the process
103
+ especially for complex tests. If this example, we can remove the `ERROR` outcome
104
+ and replace it with `default: ERROR` so that any scenario that don't fall into
105
+ any of the defined outcomes, will result to the default.
106
+
107
+ ```yaml
108
+ ---
109
+ specs:
110
+ '#prime?':
111
+ variables:
112
+ number: [-4, -1, 0, 1, 2, 3, 4, 5, 3331, 5551]
113
+
114
+ outcomes:
115
+ true: 2 | 3 | 5 | 3331
116
+ false: 1 | 4 | 5551
117
+ # ERROR: -4 | -1 | 0
118
+ default: ERROR
119
+ ```
120
+
121
+ Here is another simplified example of the above. In case the outcome is of
122
+ boolean type, we can omit the `default` altogether.
123
+ ```yaml
124
+ ---
125
+ specs:
126
+ '#prime?':
127
+ variables:
128
+ number: [1, 2, 3, 4, 5, 3331, 5551]
129
+
130
+ outcomes:
131
+ true: 2 | 3 | 5 | 3331
132
+ ```
133
+ The outcome will now be either `true` or `false`. Do note that the variables
134
+ list have been simplified and does not check negative numbers for demo
135
+ purpose only.
136
+
137
+ ### Optional variables for 1 to 1 outcomes.
138
+
139
+ `variables` definition can be omitted when each outcome matches each variables.
140
+
141
+ ```yaml
142
+ ---
143
+ specs:
144
+ '#person_name':
145
+ outcomes:
146
+ Will.I.Am: musician
147
+ Ford: soldier
148
+ John: personal
149
+ ```
150
+
151
+ ### Detecting invalid rule where a scenario matches multiple outcomes
152
+
153
+ In this example, the scenario `1` is defined to result in both `true`, and
154
+ `false`, which is impossible.
155
+
156
+ ```yaml
157
+ ---
158
+ specs:
159
+ '#positive?':
160
+ variables: {number: [-1, 0, 1]}
161
+ outcomes: {true: 1, false: 1 | -1 | 0}
162
+ ```
163
+
164
+ It will result in an error.
165
+
166
+ ```
167
+ RuntimeError:
168
+ #positive? [1] must fall into a unique rule outcome/clause, matched: ["true", "false"]
169
+ ```
170
+
171
+ In the same way if a scenario does not fall into any outcome, a similar error
172
+ will be displayed.
173
+
174
+ ```
175
+ RuntimeError:
176
+ #positive? [1] must fall into a unique rule outcome/clause, matched: []
177
+ ```
178
+
179
+ ### Using special characters as part of variable tokens.
180
+
181
+ An array may be used as variable token, this will allow for the special
182
+ characters like `!` to be used as part of the token without confusing the rule
183
+ engine.
184
+
185
+ ```yaml
186
+ ---
187
+ specs:
188
+ '#identify_sentence_type':
189
+ variables:
190
+ - "Let's do it!"
191
+ - "Will this work?"
192
+ - "Let's make a statement"
193
+ ...
194
+
195
+ ```
196
+
197
+ The rules must then be written in a different way, as arrays.
198
+
199
+ ```yaml
200
+ ...
201
+ outcomes:
202
+ exclamation: ["Let's do it!"]
203
+ question: ['Will this work?']
204
+ statement: ["Let's make a statement"]
205
+ ```
206
+
207
+ If an operation is involved:
208
+
209
+ ```yaml
210
+ outcomes:
211
+ non-question: ["Let's do it!", '|', "Let's make a statement"]
212
+ ```
213
+
214
+ ## The spec file
215
+
216
+ ### Stubbing
217
+
218
+ In this example, stubbing is done as you normally would. A `prepare` block is an
219
+ optional block to organize the parts of the test into preparation and execution
220
+ parts.
221
+
222
+ [worker_spec.rb](./spec/examples/worker_spec.rb)
223
+
224
+ ```ruby
225
+ # spec/examples/worker_spec.rb
226
+ rast Worker do
227
+ spec '#goto_work?' do
228
+ prepare do |day_type, dow|
229
+ allow(subject).to receive(:day_of_week) { dow.to_sym }
230
+ allow(subject).to receive(:holiday?) { day_type == 'Holiday' }
231
+ end
232
+
233
+ execute { subject.goto_work? ? :Work : :Rest }
234
+ end
235
+ end
236
+ ```
237
+
238
+ ### Using a factory
239
+
240
+ See [examples/factory_example.rb](./examples/factory_example.rb)
241
+
242
+ ```ruby
243
+ rast FactoryExample do
244
+ spec '#person_name' do
245
+ prepare do |service_type|
246
+ subject.instance_variable_set(:@person, build(service_type.to_sym))
247
+ end
248
+
249
+ execute { subject.person_name }
250
+ end
251
+ end
252
+ ```
253
+
254
+ ### Using doubles
255
+
256
+ Suppose we have a HotelFinder class that has a dependency to air conditioning
257
+ and security
258
+
259
+ ```ruby
260
+ rast HotelFinder do
261
+ spec '#applicable?' do
262
+ prepare do |with_ac, is_opererational, with_security, security_grade|
263
+ if with_ac
264
+ allow(subject)
265
+ .to receive(:aircon) { double(operational?: is_opererational) }
266
+ end
267
+
268
+ if with_security
269
+ allow(subject)
270
+ .to receive(:security) { double(grade: security_grade.to_sym) }
271
+ end
272
+ end
273
+
274
+ execute { subject.applicable? ? :PASSED : :INADEQUATE }
275
+ end
276
+ end
277
+
278
+ ```
279
+
280
+ ### Spec file without yaml
281
+
282
+ If a single spec is preferred, like in cases where it's much simplier,
283
+ the required configuration can be written with similar name except for the
284
+ `inclusion` and `exclusion`. The difference is due to the name clash with the
285
+ `include` keyword in ruby.
286
+
287
+ ```ruby
288
+ rast Positive do
289
+ spec '#positive?' do
290
+ variables({ number: [-1, 0, 1, 2, 3] })
291
+ outcomes(true: 1)
292
+ inclusion('!3')
293
+ exclusion(2)
294
+ execute { |number| subject.positive?(number) }
295
+ end
296
+ end
297
+ ```
data/Gemfile CHANGED
@@ -4,13 +4,11 @@ source 'https://rubygems.org'
4
4
 
5
5
  git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
6
 
7
-
8
7
  group :development, :test do
9
8
  gem 'factory_girl', '~> 4.7'
10
9
  end
11
10
 
12
11
  group :test do
13
-
14
12
  gem 'listen', '>= 3.0.8'
15
13
  gem 'lumberjack', '~> 1.0', '>= 1.0.13'
16
14
  gem 'rb-inotify', '~> 0.9.10'
@@ -0,0 +1,122 @@
1
+ # Getting Started (WIP)
2
+
3
+
4
+ ## Tutorials
5
+
6
+
7
+
8
+ ### Writing a simple test.
9
+
10
+ Suppose we want to create a class that checks if a number is a positive number or not.
11
+
12
+ #### Create a spec file `spec/positive_spec.rb`
13
+
14
+ ```ruby
15
+ require 'rast'
16
+
17
+ rast Positive do
18
+ spec 'Is Positive Example' do
19
+ execute { |number| subject.positive?(number) }
20
+ end
21
+ end
22
+ ```
23
+
24
+ - On line: 1, the library is required:
25
+
26
+ ```ruby
27
+ require 'rast'
28
+ ```
29
+
30
+ - On line: 3, invoke the DSL:
31
+
32
+ ```ruby
33
+ rast Positive do
34
+ ```
35
+ `Positive` is the fully qualified name of the class or the module to be tested.
36
+
37
+ - On line: 4, define the first spec:
38
+
39
+ ```ruby
40
+ spec 'Is Positive Example' do
41
+ ```
42
+
43
+ `'Is Positive Example'` is a descriptive identifier for the test. It can be the name of the method like `#positive?`.
44
+
45
+ - On line: 5, define the `execute` block..
46
+
47
+ ```ruby
48
+ execute { |number| subject.positive?(number) }
49
+ ```
50
+
51
+ There is a lot going on here.<br>
52
+ - Inside the block, we define how our subject will be invoked. The `positive?` method is invoked on the `subject`
53
+ accepting a parameter.
54
+ - Block parameter `number` will contain the test scenario we want to execute. More on this later. Just know that whichever values are passed here, the `subject` has to be able to handle it correctly.
55
+ - `subject` is an instance of the module or class that we defined on line: 3.
56
+ - And lastly, `execute` block expects a return value that will be verified when the spec is run.
57
+
58
+ #### Step 2: Defining outcomes and the variables that affects the test.
59
+
60
+ For the sake of simplicity, we will define the variables and outcomes in a separate yaml file, mainly because the next
61
+ steps are purely configurations. Using yaml is completely optional.
62
+
63
+ Create a folder `rast` in the same level as the spec file:
64
+
65
+ - spec
66
+ - rast
67
+
68
+ Create a yaml file with the same name as the spec, but with `.yml` extension.
69
+
70
+ `spec/rast/positive_spec.yml` would contain:
71
+
72
+ ```yaml
73
+ specs:
74
+ Is Positive Exaple:
75
+ variables: {number: [-1, 0, 1]}
76
+ outcomes: {true: 1}
77
+ ```
78
+
79
+ - On line: 1 is the root configuration element `specs` that will contain one or more specs.
80
+ - On line: 2 `Is Positive Example` is the spec identifier, this must match what we've defined on `positive_spec.rb:4`
81
+ - On line: 3, The variables that affect the SUT are defined, in this case there is only one variable called `number`,
82
+ which has 3 different possibilities `-1`, `0`, and `1`.
83
+ - On line: 4, This is where outcome to rule mapping is defined.
84
+
85
+ ### The implementation file
86
+
87
+ positive.rb will have:
88
+
89
+ ```ruby
90
+ class Positive
91
+ def positive?(number)
92
+ number > 0
93
+ end
94
+ end
95
+ ```
96
+
97
+ ## How To's
98
+
99
+ ### Use token subscript when there's a variable token clash.
100
+
101
+ In cases where multiple variables, `left` and `right`, has the same set of tokens `false` and `true`:
102
+
103
+ ```yaml
104
+ variables:
105
+ left: [false, true]
106
+ right: [false, true]
107
+ ```
108
+
109
+ We need a way to uniquely identify the tokens in the `outcomes` configuration. We can do so by using a subscript in the format: `token[n]`
110
+ For Example:
111
+
112
+ ```yaml
113
+ outcomes: {true: 'true[0] & true[1]'}
114
+ ```
115
+
116
+ The subscript `0` would refer to the `true` token in the `left` variable and subscript `1` would refer to the `true`
117
+ token in the `right` variable.
118
+
119
+
120
+ ## References
121
+
122
+ ### Outcomes
data/Getting-Started.md CHANGED
@@ -1,26 +1,12 @@
1
- # Getting Started
1
+ # Getting Started (WIP)
2
2
 
3
-
4
-
5
- ## Tutorials
6
-
7
-
8
-
9
- ### Writing a simple test.
3
+ ### A Basic Example
10
4
 
11
5
  Suppose we want to create a class that checks if a number is a positive number or not.
12
6
 
13
- #### Step 1: Defining how to exercise the system under test.
14
-
15
- Start by creating a spec file inside your `spec` folder, let's call it `positive_spec.rb`
16
-
17
- - spec
18
- - positive_spec.rb
7
+ #### Create a spec file `spec/positive_spec.rb`
19
8
 
20
-
21
- `positive_spec.rb` would look like this:
22
-
23
- ```
9
+ ```ruby
24
10
  require 'rast'
25
11
 
26
12
  rast Positive do
@@ -30,72 +16,19 @@ rast Positive do
30
16
  end
31
17
  ```
32
18
 
33
- - On line: 1, the library is required:
34
-
35
- ```
36
- require 'rast'
37
- ```
38
-
39
- - On line: 3, invoke the DSL:
40
-
41
- ```
42
- rast Positive do
43
- ```
44
- `Positive` is the fully qualified name of the class or the module to be tested.
45
-
46
- - On line: 4, define the first spec:
47
-
48
- ```
49
- spec 'Is Positive Example' do
50
- ```
51
-
52
- `'Is Positive Example'` is a descriptive identifier for the test. It can be the name of the method like `#positive?`.
53
-
54
- - On line: 5, define the `execute` block..
55
-
56
- ```
57
- execute { |number| subject.positive?(number) }
58
- ```
59
-
60
- There is a lot going on here.<br>
61
- - Inside the block, we define how our subject will be invoked. The `positive?` method is invoked on the `subject`
62
- accepting a parameter.
63
- - Block parameter `number` will contain the test scenario we want to execute. More on this later. Just know that whichever values are passed here, the `subject` has to be able to handle it correctly.
64
- - `subject` is an instance of the module or class that we defined on line: 3.
65
- - And lastly, `execute` block expects a return value that will be verified when the spec is run.
66
-
67
- #### Step 2: Defining outcomes and the variables that affects the test.
68
-
69
- For the sake of simplicity, we will define the variables and outcomes in a separate yaml file, mainly because the next
70
- steps are purely configurations. Using yaml is completely optional.
71
-
72
- Create a folder `rast` in the same level as the spec file:
73
-
74
- - spec
75
- - rast
19
+ #### Create a spec configuration `spec/rast/positive_spec.yml`
76
20
 
77
- Create a yaml file with the same name as the spec, but with `.yml` extension.
78
-
79
- `spec/rast/positive_spec.yml` would contain:
80
-
81
- ```
21
+ ```yaml
82
22
  specs:
83
23
  Is Positive Exaple:
84
24
  variables: {number: [-1, 0, 1]}
85
25
  outcomes: {true: 1}
86
26
  ```
87
27
 
88
- - On line: 1 is the root configuration element `specs` that will contain one or more specs.
89
- - On line: 2 `Is Positive Example` is the spec identifier, this must match what we've defined on `positive_spec.rb:4`
90
- - On line: 3, The variables that affect the SUT are defined, in this case there is only one variable called `number`,
91
- which has 3 different possibilities `-1`, `0`, and `1`.
92
- - On line: 4, This is where outcome to rule mapping is defined.
93
-
94
- ### The implementation file
28
+ The class to test:
95
29
 
96
- positive.rb will have:
97
-
98
- ```
30
+ ```ruby
31
+ # positive.rb
99
32
  class Positive
100
33
  def positive?(number)
101
34
  number > 0
@@ -103,37 +36,21 @@ class Positive
103
36
  end
104
37
  ```
105
38
 
106
- ## How To's
39
+ Running the test:
107
40
 
108
- ### Use token subscript when there's a variable token clash.
41
+ `$ rspec -fd spec/examples/positive_spec.rb`
109
42
 
110
- In cases where multiple variables, `left` and `right`, has the same set of tokens `false` and `true`:
43
+ Test result:
111
44
 
112
45
  ```
113
- variables:
114
- left: [false, true]
115
- right: [false, true]
116
- ```
117
-
118
- We need a way to uniquely identify the tokens in the `outcomes` configuration. We can do so by using a subscript in the format: `token[n]`
119
- For Example:
46
+ Positive: #positive?
47
+ [false]=[number: -1]
48
+ [false]=[number: 0]
49
+ [true]=[number: 1]
120
50
 
51
+ Finished in 0.00471 seconds (files took 0.47065 seconds to load)
52
+ 3 examples, 0 failures
121
53
  ```
122
- outcomes: {true: 'true[0] & true[1]'}
123
- ```
124
-
125
- The subscript `0` would refer to the `true` token in the `left` variable and subscript `1` would refer to the `true`
126
- token in the `right` variable.
127
-
128
-
129
- ## References
130
-
131
- ### Outcomes
132
-
133
-
134
- ## Current Limitations.
135
-
136
-
137
-
138
54
 
55
+ ## How To's
139
56