fix 1.0.0.beta5 → 1.0.0.beta6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +13 -10
- data/lib/fix.rb +2 -1
- data/lib/fix/console.rb +2 -10
- data/lib/fix/dsl.rb +75 -79
- data/lib/fix/test.rb +29 -22
- data/lib/kernel.rb +6 -4
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 361429a943a5149aeea4da4dd83150360db392275ec4d9d98e76969e60768dce
|
4
|
+
data.tar.gz: 6df8aaff424652d4e73cb163cc1dfd973c9a4a03ec2e2d6e351db5f87b0674a8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 24a1c58a8e0db0ef62d97881eabc8a4ff0596d82c96f1dee40fc68edffdc17f0206c6d45aa11040e3b11af35038df58ee7bca1f6190330f91b1b384304dcc304
|
7
|
+
data.tar.gz: 0a5ff783abf6bce80c8ff4a3e3f2e938c004bf5cd02e8300f56650e6312638e911638ed41c9293047aa757ae73048cd412bae5b6cc031e43bab82a2f4b92e41c
|
data/README.md
CHANGED
@@ -7,6 +7,8 @@
|
|
7
7
|
[![RuboCop](https://github.com/fixrb/fix/workflows/RuboCop/badge.svg?branch=main)](https://github.com/fixrb/fix/actions?query=workflow%3Arubocop+branch%3Amain)
|
8
8
|
[![License](https://img.shields.io/github/license/fixrb/fix?label=License&logo=github)](https://github.com/fixrb/fix/raw/main/LICENSE.md)
|
9
9
|
|
10
|
+
⚠️ This project is still in the experimental phase. May be used at your own risk.
|
11
|
+
|
10
12
|
![Fix specing framework for Ruby](https://github.com/fixrb/fix/raw/main/img/fix.jpg)
|
11
13
|
|
12
14
|
## Installation
|
@@ -14,7 +16,7 @@
|
|
14
16
|
Add this line to your application's Gemfile:
|
15
17
|
|
16
18
|
```ruby
|
17
|
-
gem "fix", ">= 1.0.0.
|
19
|
+
gem "fix", ">= 1.0.0.beta6"
|
18
20
|
```
|
19
21
|
|
20
22
|
And then execute:
|
@@ -58,16 +60,18 @@ And this fixed behavior:
|
|
58
60
|
relative "fix"
|
59
61
|
|
60
62
|
Fix :Duck do
|
63
|
+
it MUST be_an_instance_of :Duck
|
64
|
+
|
61
65
|
on :swims do
|
62
|
-
it
|
66
|
+
it MUST eql "Swoosh..."
|
63
67
|
end
|
64
68
|
|
65
69
|
on :speaks do
|
66
|
-
it
|
70
|
+
it MUST raise_exception NoMethodError
|
67
71
|
end
|
68
72
|
|
69
73
|
on :sings do
|
70
|
-
it
|
74
|
+
it MAY eql "♪... ♫..."
|
71
75
|
end
|
72
76
|
end
|
73
77
|
```
|
@@ -80,7 +84,7 @@ When I run this test:
|
|
80
84
|
require_relative "app"
|
81
85
|
require_relative "fix"
|
82
86
|
|
83
|
-
Fix[:Duck].
|
87
|
+
Fix[:Duck].test { Duck.new }
|
84
88
|
```
|
85
89
|
|
86
90
|
```sh
|
@@ -90,15 +94,14 @@ ruby examples/duck/test.rb
|
|
90
94
|
I should see this output:
|
91
95
|
|
92
96
|
```txt
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
- Success: undefined method `speaks' for #<Duck:0x00007fc5289bcf68>.
|
97
|
+
NoMethodError: undefined method `sings' for #<Duck:0x00007fc5289bcf68>.
|
98
|
+
Success: expected to eql "Swoosh...".
|
99
|
+
Success: undefined method `speaks' for #<Duck:0x00007fc5289bcf68>.
|
97
100
|
```
|
98
101
|
|
99
102
|
## Test suite
|
100
103
|
|
101
|
-
__Fix__'s specifications
|
104
|
+
__Fix__'s specifications will be [fixed here](https://github.com/fixrb/fix/blob/main/fix/) and will be tested against __Fix__'s codebase executing [test/*](https://github.com/fixrb/fix/blob/main/test/)'s files.
|
102
105
|
|
103
106
|
## Contact
|
104
107
|
|
data/lib/fix.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative File.join("fix", "doc")
|
3
4
|
require_relative File.join("fix", "test")
|
4
5
|
|
5
6
|
require_relative "kernel"
|
@@ -18,6 +19,6 @@ module Fix
|
|
18
19
|
#
|
19
20
|
# @return [::Fix::Dsl] The specification document.
|
20
21
|
def self.[](name)
|
21
|
-
Test.new(name)
|
22
|
+
Test.new(*Doc.const_get(name).const_get(:CONTEXTS))
|
22
23
|
end
|
23
24
|
end
|
data/lib/fix/console.rb
CHANGED
@@ -3,21 +3,13 @@
|
|
3
3
|
module Fix
|
4
4
|
# Send log messages to the console.
|
5
5
|
module Console
|
6
|
-
# @param name [String, Symbol] The name of the specification document.
|
7
|
-
# @param subject [#object_id] The front object to be tested.
|
8
|
-
#
|
9
|
-
# @return [nil] Add a message to `$stdout`.
|
10
|
-
def self.title(name, subject)
|
11
|
-
puts "Run #{name} specs against #{subject.inspect}:"
|
12
|
-
end
|
13
|
-
|
14
6
|
# @param report [::Expresenter::Pass] Passed expectation result presenter.
|
15
7
|
#
|
16
8
|
# @see https://github.com/fixrb/expresenter
|
17
9
|
#
|
18
10
|
# @return [nil] Add a colored message to `$stdout`.
|
19
11
|
def self.passed_spec(report)
|
20
|
-
puts
|
12
|
+
puts report.colored_string
|
21
13
|
end
|
22
14
|
|
23
15
|
# @param report [::Expresenter::Fail] Failed expectation result presenter.
|
@@ -26,7 +18,7 @@ module Fix
|
|
26
18
|
#
|
27
19
|
# @raise [SystemExit] Terminate execution immediately with colored message.
|
28
20
|
def self.failed_spec(report)
|
29
|
-
abort
|
21
|
+
abort report.colored_string
|
30
22
|
end
|
31
23
|
end
|
32
24
|
end
|
data/lib/fix/dsl.rb
CHANGED
@@ -4,7 +4,7 @@ require "defi"
|
|
4
4
|
require "matchi/helper"
|
5
5
|
require "spectus"
|
6
6
|
|
7
|
-
require_relative "
|
7
|
+
require_relative "test"
|
8
8
|
|
9
9
|
module Fix
|
10
10
|
# Abstract class for handling the domain-specific language.
|
@@ -16,22 +16,10 @@ module Fix
|
|
16
16
|
# @example
|
17
17
|
# require "fix"
|
18
18
|
#
|
19
|
-
# Fix
|
19
|
+
# Fix do
|
20
20
|
# let(:name) { "Bob" }
|
21
|
-
#
|
22
|
-
# it { expect(name).to eq "Bob" }
|
23
|
-
#
|
24
|
-
# context "with last name" do
|
25
|
-
# let(:name) { "#{super()} Smith" }
|
26
|
-
#
|
27
|
-
# it { expect(name).to eq "Bob Smith" }
|
28
|
-
# end
|
29
21
|
# end
|
30
22
|
#
|
31
|
-
# # Output to the console
|
32
|
-
# # Success: expected to eq "Bob".
|
33
|
-
# # Success: expected to eq "Bob Smith".
|
34
|
-
#
|
35
23
|
# @param name [String, Symbol] The name of the property.
|
36
24
|
# @param block [Proc] The content of the method to define.
|
37
25
|
#
|
@@ -39,11 +27,28 @@ module Fix
|
|
39
27
|
#
|
40
28
|
# @api public
|
41
29
|
def self.let(name, &block)
|
42
|
-
|
30
|
+
private define_method(name, &block)
|
43
31
|
end
|
44
32
|
|
33
|
+
# Defines an example group with user-defined properties that describes a
|
34
|
+
# unit to be tested.
|
35
|
+
#
|
36
|
+
# @example
|
37
|
+
# require "fix"
|
38
|
+
#
|
39
|
+
# Fix do
|
40
|
+
# with :password, "secret" do
|
41
|
+
# it MUST equal true
|
42
|
+
# end
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# @param kwargs [Hash] The list of propreties.
|
46
|
+
# @param block [Proc] The block to define the specs.
|
47
|
+
#
|
48
|
+
# @api public
|
45
49
|
def self.with(**kwargs, &block)
|
46
50
|
klass = ::Class.new(self)
|
51
|
+
klass.const_get(:CONTEXTS) << klass
|
47
52
|
kwargs.each { |name, value| klass.let(name) { value } }
|
48
53
|
klass.instance_eval(&block)
|
49
54
|
klass
|
@@ -54,115 +59,106 @@ module Fix
|
|
54
59
|
# @example
|
55
60
|
# require "fix"
|
56
61
|
#
|
57
|
-
# Fix
|
58
|
-
# on
|
59
|
-
# it
|
62
|
+
# Fix do
|
63
|
+
# on :+, 2 do
|
64
|
+
# it MUST be 42
|
60
65
|
# end
|
61
66
|
# end
|
62
67
|
#
|
63
|
-
#
|
64
|
-
# # Success: expected to eq "foobar".
|
65
|
-
#
|
66
|
-
# @param const [Module, String] A module to include in block context.
|
68
|
+
# @param method_name [String, Symbol] The method to send to the subject.
|
67
69
|
# @param block [Proc] The block to define the specs.
|
68
70
|
#
|
69
71
|
# @api public
|
70
72
|
def self.on(method_name, *args, **kwargs, &block)
|
71
73
|
klass = ::Class.new(self)
|
72
|
-
klass.const_get(:
|
74
|
+
klass.const_get(:CONTEXTS) << klass
|
75
|
+
|
73
76
|
const_set("Child#{block.object_id}", klass)
|
74
77
|
|
75
78
|
klass.define_singleton_method(:challenges) do
|
76
79
|
super() + [::Defi.send(method_name, *args, **kwargs)]
|
77
80
|
end
|
78
81
|
|
82
|
+
def klass.initialize
|
83
|
+
if subject.raised?
|
84
|
+
subject
|
85
|
+
else
|
86
|
+
challenge = ::Defi.send(method_name, *args, **kwargs)
|
87
|
+
challenge.to(subject.call)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
79
91
|
klass.instance_eval(&block)
|
80
92
|
klass
|
81
93
|
end
|
82
94
|
|
83
|
-
# Defines a concrete
|
95
|
+
# Defines a concrete spec definition.
|
84
96
|
#
|
85
|
-
#
|
86
|
-
#
|
87
|
-
# @example The integer after 41
|
88
|
-
# require "fix"
|
89
|
-
#
|
90
|
-
# Fix(:AnswerToEverything) { it { MUST be 42 } }.call(42)
|
91
|
-
#
|
92
|
-
# # Output to the console
|
93
|
-
# # Success: expected to be 42.
|
94
|
-
#
|
95
|
-
# @example A division by zero
|
97
|
+
# @example
|
96
98
|
# require "fix"
|
97
99
|
#
|
98
|
-
# Fix
|
99
|
-
# it { MUST be_an_instance_of Integer }
|
100
|
-
#
|
101
|
-
# on :/, 0 do
|
102
|
-
# it { MUST raise_exception ZeroDivisionError }
|
103
|
-
# end
|
104
|
-
# end
|
105
|
-
#
|
106
|
-
# Fix[:Integer].call(41)
|
107
|
-
#
|
108
|
-
# # Output to the console
|
109
|
-
# # Success: expected 41 to be an instance of Integer.
|
110
|
-
# # Success: divided by 0.
|
111
|
-
#
|
112
|
-
# It can be used inside a {.describe} or {.context} section.
|
113
|
-
#
|
114
|
-
# @param block [Proc] An expectation to evaluate.
|
115
|
-
#
|
116
|
-
# @raise (see ExpectationTarget::Base#result)
|
117
|
-
# @return (see ExpectationTarget::Base#result)
|
100
|
+
# Fix { it MUST equal 42 }
|
118
101
|
#
|
119
102
|
# @api public
|
120
|
-
def self.it(
|
121
|
-
define_method("test_#{
|
103
|
+
def self.it(requirement)
|
104
|
+
define_method("test_#{requirement.object_id}") { requirement }
|
122
105
|
end
|
123
106
|
|
124
|
-
|
107
|
+
# @todo Move this method inside "fix-its" gem.
|
108
|
+
def self.its(method_name, requirement)
|
125
109
|
klass = ::Class.new(self)
|
126
|
-
klass.const_get(:
|
127
|
-
|
110
|
+
klass.const_get(:CONTEXTS) << klass
|
111
|
+
|
112
|
+
const_set("Child#{requirement.object_id}", klass)
|
128
113
|
|
129
114
|
klass.define_singleton_method(:challenges) do
|
130
|
-
super() + [::Defi.send(method_name
|
115
|
+
super() + [::Defi.send(method_name)]
|
131
116
|
end
|
132
117
|
|
133
|
-
klass.
|
118
|
+
def klass.initialize
|
119
|
+
if subject.raised?
|
120
|
+
subject
|
121
|
+
else
|
122
|
+
challenge = ::Defi.send(method_name, *args, **kwargs)
|
123
|
+
challenge.to(subject.call)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
klass.it(requirement)
|
134
128
|
end
|
135
129
|
|
136
130
|
def self.challenges
|
137
131
|
[]
|
138
132
|
end
|
139
133
|
|
134
|
+
def self.test(&subject)
|
135
|
+
Test.new(self).test(&subject)
|
136
|
+
end
|
137
|
+
|
140
138
|
private_class_method :challenges
|
141
139
|
|
142
140
|
private
|
143
141
|
|
144
|
-
|
145
|
-
@subject = subject
|
146
|
-
end
|
142
|
+
attr_reader :subject
|
147
143
|
|
148
|
-
|
149
|
-
|
150
|
-
::Spectus.public_send(method_name, matcher).call do
|
151
|
-
self.class.send(:challenges).inject(@subject) do |object, challenge|
|
152
|
-
challenge.to(object).call
|
153
|
-
end
|
154
|
-
end
|
155
|
-
end
|
144
|
+
def initialize(&subject)
|
145
|
+
@subject = ::Defi::Value.new(&subject)
|
156
146
|
end
|
157
147
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
148
|
+
::Matchi::Matcher.constants.each do |matcher_const|
|
149
|
+
next if matcher_const.equal?(:Base)
|
150
|
+
|
151
|
+
matcher_klass = ::Matchi::Matcher.const_get(matcher_const)
|
152
|
+
|
153
|
+
define_singleton_method(matcher_klass.to_sym) do |*args|
|
154
|
+
matcher_klass.new(*args)
|
155
|
+
end
|
162
156
|
end
|
163
157
|
|
164
|
-
|
165
|
-
|
158
|
+
::Spectus.methods(false).each do |method_name|
|
159
|
+
define_singleton_method(method_name.upcase) do |matcher|
|
160
|
+
::Spectus.public_send(method_name, matcher)
|
161
|
+
end
|
166
162
|
end
|
167
163
|
end
|
168
164
|
end
|
data/lib/fix/test.rb
CHANGED
@@ -8,37 +8,44 @@ require_relative "doc"
|
|
8
8
|
module Fix
|
9
9
|
# Module for testing spec documents.
|
10
10
|
class Test
|
11
|
-
attr_reader :
|
11
|
+
attr_reader :contexts
|
12
12
|
|
13
|
-
def initialize(
|
14
|
-
@
|
13
|
+
def initialize(*contexts)
|
14
|
+
@contexts = contexts
|
15
15
|
end
|
16
16
|
|
17
|
-
def
|
18
|
-
|
19
|
-
requirements(subject).each { |requirement| test(requirement) }
|
17
|
+
def test(&block)
|
18
|
+
requirements(&block)
|
20
19
|
exit(true)
|
21
20
|
end
|
22
21
|
|
23
22
|
private
|
24
23
|
|
25
|
-
def requirements(
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
24
|
+
def requirements(&block)
|
25
|
+
contexts.flat_map do |context|
|
26
|
+
sandbox = context.new
|
27
|
+
sandbox.public_methods(false).shuffle.map do |public_method|
|
28
|
+
definition = sandbox.public_send(public_method)
|
29
|
+
|
30
|
+
report = begin
|
31
|
+
definition.call do
|
32
|
+
front_object = instance_eval(&block)
|
33
|
+
|
34
|
+
context.send(:challenges).inject(front_object) do |object, challenge|
|
35
|
+
challenge.to(object).call
|
36
|
+
end
|
37
|
+
end
|
38
|
+
rescue ::Expresenter::Fail => e
|
39
|
+
e
|
40
|
+
end
|
41
|
+
|
42
|
+
if report.passed?
|
43
|
+
Console.passed_spec report
|
44
|
+
else
|
45
|
+
Console.failed_spec report
|
46
|
+
end
|
30
47
|
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def specs
|
35
|
-
Doc.const_get(name).const_get(:SPECS)
|
36
|
-
end
|
37
|
-
|
38
|
-
def test(requirement)
|
39
|
-
Console.passed_spec requirement.call
|
40
|
-
rescue ::Expresenter::Fail => e
|
41
|
-
Console.failed_spec e
|
48
|
+
end
|
42
49
|
end
|
43
50
|
end
|
44
51
|
end
|
data/lib/kernel.rb
CHANGED
@@ -19,12 +19,14 @@ module Kernel
|
|
19
19
|
# @return [Class] The specification document.
|
20
20
|
#
|
21
21
|
# rubocop:disable Naming/MethodName
|
22
|
-
def Fix(name, &block)
|
22
|
+
def Fix(name = nil, &block)
|
23
23
|
klass = ::Class.new(::Fix::Dsl)
|
24
|
-
klass.const_set(:
|
24
|
+
klass.const_set(:CONTEXTS, [klass])
|
25
25
|
klass.instance_eval(&block)
|
26
|
-
|
27
|
-
::Fix::
|
26
|
+
|
27
|
+
::Fix::Doc.const_set(name, klass) unless name.nil?
|
28
|
+
|
29
|
+
::Fix::Test.new(*klass.const_get(:CONTEXTS))
|
28
30
|
end
|
29
31
|
# rubocop:enable Naming/MethodName
|
30
32
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fix
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.beta6
|
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-07-
|
11
|
+
date: 2021-07-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: defi
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 2.
|
33
|
+
version: 2.2.0
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 2.
|
40
|
+
version: 2.2.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: spectus
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|