synthesis 0.0.7 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +0 -0
- data/README +5 -3
- data/Rakefile +1 -1
- data/lib/synthesis/adapter/expectations.rb +14 -5
- data/lib/synthesis/adapter/mocha.rb +14 -5
- data/lib/synthesis/adapter/rspec.rb +14 -5
- data/lib/synthesis/adapter.rb +12 -41
- data/lib/synthesis/class.rb +2 -2
- data/lib/synthesis/expectation.rb +39 -24
- data/lib/synthesis/expectation_interceptor.rb +57 -0
- data/lib/synthesis/expectation_matcher.rb +23 -0
- data/lib/synthesis/expectation_record_enabled.rb +32 -0
- data/lib/synthesis/method_invocation_watcher.rb +2 -2
- data/lib/synthesis/module.rb +2 -2
- data/lib/synthesis/object.rb +2 -2
- data/lib/synthesis/recordable.rb +10 -8
- data/lib/synthesis/reporter.rb +24 -0
- data/lib/synthesis/runner.rb +1 -2
- data/lib/synthesis.rb +4 -1
- data/test/synthesis/adapter/mocha/helper.rb +2 -1
- data/test/synthesis/adapter/mocha/mocha_expectation_test.rb +12 -5
- data/test/synthesis/adapter/rspec/helper.rb +2 -1
- data/test/synthesis/expectation_matcher_test.rb +54 -0
- data/test/synthesis/expectation_record_test.rb +11 -10
- data/test/synthesis/expectation_test.rb +45 -46
- data/test/synthesis/method_invocation_watcher_test.rb +2 -2
- data/test/synthesis/recordable_test.rb +2 -2
- data/test_project/mocha/test/data_brander_test.rb +1 -1
- metadata +8 -4
- data/lib/synthesis/report.rb +0 -22
data/COPYING
CHANGED
File without changes
|
data/README
CHANGED
@@ -2,7 +2,9 @@
|
|
2
2
|
|
3
3
|
== Philosophy
|
4
4
|
|
5
|
-
Currently we believe that developers are writing unnecessary
|
5
|
+
Currently we believe that developers are writing unnecessary <em>dependency wired</em> tests to cover uncertainty about the validity of simulated interactions in their <em>dependency neutral</em> tests. In other words, we cannot be certain that all our simulated interaction based tests 'join up'. If it were possible to correlate the simulated interactions in our tests, then we should be able to do away with the need to write large numbers of complex, slow and brittle wired tests (apart from those which interact with the boundaries of the SUT).
|
6
|
+
|
7
|
+
Synthesis combines lightweight, <em>dependency neutral</em> tests to build confidence that the system under test is complete and reduces the need for large, overarching tests.
|
6
8
|
|
7
9
|
== Installation
|
8
10
|
|
@@ -43,13 +45,13 @@ To use with Test::Unit and Mocha, ignoring Array and Hash:
|
|
43
45
|
t.ignore Array, Hash
|
44
46
|
end
|
45
47
|
|
46
|
-
To use with RSpec, running all specs in the <tt>
|
48
|
+
To use with RSpec, running all specs in the <tt>spec</tt> directory:
|
47
49
|
|
48
50
|
require "synthesis/task"
|
49
51
|
|
50
52
|
Synthesis::Task.new do |t|
|
51
53
|
t.adapter = :rspec
|
52
|
-
t.pattern = '
|
54
|
+
t.pattern = 'spec/**/*_spec.rb'
|
53
55
|
end
|
54
56
|
|
55
57
|
To use with Expectations, redirecting output to a file
|
data/Rakefile
CHANGED
@@ -6,14 +6,23 @@ require File.dirname(__FILE__) + "/../../synthesis"
|
|
6
6
|
|
7
7
|
module Synthesis
|
8
8
|
class ExpectationsAdapter < Adapter
|
9
|
-
|
10
|
-
ignore_instances_of Class::AnyInstance
|
11
|
-
expectation_class Mocha::Expectation
|
12
|
-
intercept :method => :expects, :on => Object
|
13
|
-
|
14
9
|
def run
|
15
10
|
fail_unless { Expectations::SuiteRunner.instance.suite.execute }
|
16
11
|
end
|
12
|
+
|
13
|
+
def collect_expectations
|
14
|
+
ignore_instances_of Class::AnyInstance
|
15
|
+
Object.extend(ExpectationRecordEnabled)
|
16
|
+
Object.record_expectations_on(:expects)
|
17
|
+
Mocha::Expectation.extend(ExpectationInterceptor)
|
18
|
+
Mocha::Expectation.intercept_expected_argument_types_on(:with)
|
19
|
+
Mocha::Expectation.intercept_expected_return_values_on(:returns)
|
20
|
+
end
|
21
|
+
|
22
|
+
def stop_collecting_expectations
|
23
|
+
Mocha::Expectation.reset!
|
24
|
+
Object.reset!
|
25
|
+
end
|
17
26
|
end
|
18
27
|
end
|
19
28
|
|
@@ -6,14 +6,23 @@ require File.dirname(__FILE__) + "/../../synthesis"
|
|
6
6
|
|
7
7
|
module Synthesis
|
8
8
|
class MochaAdapter < Adapter
|
9
|
-
|
10
|
-
ignore_instances_of Class::AnyInstance
|
11
|
-
expectation_class Mocha::Expectation
|
12
|
-
intercept :method => :expects, :on => Object
|
13
|
-
|
14
9
|
def run
|
15
10
|
Test::Unit.run = true # Yes means no...
|
16
11
|
fail_unless { Test::Unit::AutoRunner.run }
|
17
12
|
end
|
13
|
+
|
14
|
+
def collect_expectations
|
15
|
+
ignore_instances_of Class::AnyInstance
|
16
|
+
Object.extend(ExpectationRecordEnabled)
|
17
|
+
Object.record_expectations_on(:expects)
|
18
|
+
Mocha::Expectation.extend(ExpectationInterceptor)
|
19
|
+
Mocha::Expectation.intercept_expected_argument_types_on(:with)
|
20
|
+
Mocha::Expectation.intercept_expected_return_values_on(:returns)
|
21
|
+
end
|
22
|
+
|
23
|
+
def stop_collecting_expectations
|
24
|
+
Mocha::Expectation.reset!
|
25
|
+
Object.reset!
|
26
|
+
end
|
18
27
|
end
|
19
28
|
end
|
@@ -6,14 +6,23 @@ require File.dirname(__FILE__) + "/../../synthesis"
|
|
6
6
|
|
7
7
|
module Synthesis
|
8
8
|
class RSpecAdapter < Adapter
|
9
|
-
|
10
|
-
ignore_instances_of Spec::Mocks::Mock
|
11
|
-
expectation_class Spec::Mocks::MessageExpectation
|
12
|
-
intercept :method => :should_receive, :on => Spec::Mocks::Methods
|
13
|
-
|
14
9
|
def run
|
15
10
|
rspec_options.files.clear
|
16
11
|
fail_unless { rspec_options.run_examples }
|
17
12
|
end
|
13
|
+
|
14
|
+
def collect_expectations
|
15
|
+
ignore_instances_of Spec::Mocks::Mock
|
16
|
+
Spec::Mocks::Methods.extend(ExpectationRecordEnabled)
|
17
|
+
Spec::Mocks::Methods.record_expectations_on(:should_receive)
|
18
|
+
Spec::Mocks::MessageExpectation.extend(ExpectationInterceptor)
|
19
|
+
Spec::Mocks::MessageExpectation.intercept_expected_argument_types_on(:with)
|
20
|
+
Spec::Mocks::MessageExpectation.intercept_expected_return_values_on(:and_return)
|
21
|
+
end
|
22
|
+
|
23
|
+
def stop_collecting_expectations
|
24
|
+
Spec::Mocks::MessageExpectation.reset!
|
25
|
+
Spec::Mocks::Methods.reset!
|
26
|
+
end
|
18
27
|
end
|
19
28
|
end
|
data/lib/synthesis/adapter.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
module Synthesis
|
2
|
-
# Subclasses of Adapter must implement the
|
3
|
-
#
|
4
|
-
# Synthesis::
|
2
|
+
# Subclasses of Adapter must implement the run, collect_expectations and
|
3
|
+
# stop_collecting_expectations methods.
|
4
|
+
# For example implementations refer to Synthesis::MochaAdapter and
|
5
|
+
# Synthesis::RSpecAdapter.
|
5
6
|
class Adapter
|
6
7
|
include Logging
|
7
8
|
|
@@ -13,52 +14,22 @@ module Synthesis
|
|
13
14
|
# once for verifying the collected expectations.
|
14
15
|
def fail_unless(&block)
|
15
16
|
log "Collecting expectations..."
|
17
|
+
collect_expectations
|
16
18
|
Dir[@pattern].each { |t| require t }
|
17
19
|
exit -1 unless yield
|
18
20
|
log "Verifying expectation invocations..."
|
21
|
+
stop_collecting_expectations
|
19
22
|
ExpectationRecord.record_invocations
|
20
23
|
yield
|
21
24
|
end
|
22
25
|
|
26
|
+
# The type of object representing a mock or stub for the test framework.
|
27
|
+
# Objects of this type will be ignored by Synthesis.
|
28
|
+
def ignore_instances_of(type)
|
29
|
+
Synthesis.const_set(:MOCK_OBJECT, type)
|
30
|
+
end
|
31
|
+
|
23
32
|
class << self
|
24
|
-
# The type of object representing a mock or stub for the test framework.
|
25
|
-
# Objects of this type will be ignored by Synthesis.
|
26
|
-
def ignore_instances_of(type)
|
27
|
-
Synthesis.const_set(:MOCK_OBJECT, type)
|
28
|
-
end
|
29
|
-
|
30
|
-
# The class representing a mock object expectation in the test framework.
|
31
|
-
# Synthesis will intercept its "with" method in order to collect the
|
32
|
-
# expectation method parameters being passed to it.
|
33
|
-
def expectation_class(klass)
|
34
|
-
klass.class_eval do
|
35
|
-
attr_accessor :synthesis_expectation
|
36
|
-
|
37
|
-
alias original_with with
|
38
|
-
|
39
|
-
def with(*expected_parameters, &matching_block)
|
40
|
-
synthesis_expectation.args = expected_parameters if synthesis_expectation
|
41
|
-
original_with *expected_parameters, &matching_block
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
# Accepts a Hash parameter with two entries, :method and :on. These
|
47
|
-
# represent the name of the method used for declaring a method interaction
|
48
|
-
# expectation on an object and the name of the object the method is defined
|
49
|
-
# on respectively. Synthesis will intercept this method in order to collect
|
50
|
-
# simulated method interactions.
|
51
|
-
def intercept(params)
|
52
|
-
method_name, obj = params[:method], params[:on]
|
53
|
-
obj.send(:alias_method, :original_meth, method_name)
|
54
|
-
obj.send(:define_method, method_name) do |meth|
|
55
|
-
s_expectation = Synthesis::ExpectationRecord.add_expectation(self, meth, caller[0])
|
56
|
-
m_expectation = original_meth(meth)
|
57
|
-
m_expectation.synthesis_expectation = s_expectation
|
58
|
-
m_expectation
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
33
|
def inherited(subclass)
|
63
34
|
@adapter = subclass
|
64
35
|
end
|
data/lib/synthesis/class.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
class Class
|
2
|
-
def expectation(method, track, args = [])
|
3
|
-
Synthesis::Expectation::Singleton.new(self, method, track, args)
|
2
|
+
def expectation(method, track, args = [], return_value = nil)
|
3
|
+
Synthesis::Expectation::Singleton.new(self, method, track, args, return_value)
|
4
4
|
end
|
5
5
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Synthesis
|
2
2
|
module Expectation
|
3
|
-
def self.new(receiver, method, track, args = [])
|
4
|
-
receiver.expectation
|
3
|
+
def self.new(receiver, method, track, args = [], return_values = [])
|
4
|
+
receiver.expectation(method, track, args, return_values)
|
5
5
|
end
|
6
6
|
|
7
7
|
class Expectation
|
@@ -9,13 +9,18 @@ module Synthesis
|
|
9
9
|
attr_reader :receiver, :method
|
10
10
|
attr_accessor :args
|
11
11
|
|
12
|
-
def initialize(receiver, method, track, args)
|
12
|
+
def initialize(receiver, method, track, args, return_values)
|
13
13
|
@receiver, @method, @track, @args = receiver, method, track, args
|
14
|
+
@return_values = return_values
|
14
15
|
end
|
15
16
|
|
16
17
|
def record_invocations
|
17
|
-
meta_receiver.extend
|
18
|
-
meta_receiver.recordable_method
|
18
|
+
meta_receiver.extend(Recordable)
|
19
|
+
meta_receiver.recordable_method(@method)
|
20
|
+
end
|
21
|
+
|
22
|
+
def eql?(other)
|
23
|
+
ExpectationMatcher.new(self, other).match?
|
19
24
|
end
|
20
25
|
|
21
26
|
def ==(other)
|
@@ -30,10 +35,21 @@ module Synthesis
|
|
30
35
|
@invoked = true
|
31
36
|
end
|
32
37
|
|
33
|
-
|
38
|
+
def arg_types
|
39
|
+
args.map { |arg| arg.class }
|
40
|
+
end
|
34
41
|
|
35
|
-
def
|
36
|
-
|
42
|
+
def return_value_types
|
43
|
+
@return_values.map { |val| val.class }.uniq
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_return_values(*vals)
|
47
|
+
@return_values_defined = true
|
48
|
+
@return_values += vals
|
49
|
+
end
|
50
|
+
|
51
|
+
def return_values_defined?
|
52
|
+
@return_values_defined
|
37
53
|
end
|
38
54
|
end
|
39
55
|
|
@@ -42,19 +58,18 @@ module Synthesis
|
|
42
58
|
@receiver.__metaclass__
|
43
59
|
end
|
44
60
|
|
45
|
-
def eql?(other)
|
46
|
-
return false unless other.is_a?(Synthesis::Expectation::Singleton)
|
47
|
-
@receiver.name == other.receiver.name &&
|
48
|
-
@method == other.method &&
|
49
|
-
args_match?(other)
|
50
|
-
end
|
51
|
-
|
52
61
|
def hash
|
53
62
|
(@receiver.name.hash * 23) + @method.hash
|
54
63
|
end
|
55
64
|
|
65
|
+
def receiver_class
|
66
|
+
@receiver
|
67
|
+
end
|
68
|
+
|
56
69
|
def to_s
|
57
|
-
"
|
70
|
+
"(#{return_value_types * ", "}) " +
|
71
|
+
"#{@receiver.name}.#{@method}(#{@args.map { |arg| arg.class } * ', '})" +
|
72
|
+
"in #{@track}"
|
58
73
|
end
|
59
74
|
end
|
60
75
|
|
@@ -63,19 +78,19 @@ module Synthesis
|
|
63
78
|
@receiver.class
|
64
79
|
end
|
65
80
|
|
66
|
-
def eql?(other)
|
67
|
-
return false unless other.is_a?(Synthesis::Expectation::Instance)
|
68
|
-
meta_receiver.name == other.meta_receiver.name &&
|
69
|
-
@method == other.method &&
|
70
|
-
args_match?(other)
|
71
|
-
end
|
72
|
-
|
73
81
|
def hash
|
74
82
|
(meta_receiver.name.hash * 23) + @method.hash
|
75
83
|
end
|
76
84
|
|
85
|
+
def receiver_class
|
86
|
+
meta_receiver
|
87
|
+
end
|
88
|
+
|
77
89
|
def to_s
|
78
|
-
"
|
90
|
+
"(#{return_value_types * ", "}) " +
|
91
|
+
"#{meta_receiver.name}.new.#{@method}" +
|
92
|
+
"(#{@args.map { |arg| arg.class } * ', '})" +
|
93
|
+
"in #{@track}"
|
79
94
|
end
|
80
95
|
end
|
81
96
|
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Synthesis
|
2
|
+
# Extend by the mock object framework's expectation mechanism to allow
|
3
|
+
# Synthesis to tap into it in order to collect simulated method arguments
|
4
|
+
# and return values.
|
5
|
+
module ExpectationInterceptor
|
6
|
+
# Intercept the mock object framework's expectation method for declaring a mocked
|
7
|
+
# method's arguments so that Synthesis can record them.
|
8
|
+
def intercept_expected_argument_types_on(method_name)
|
9
|
+
@original_with = method_name
|
10
|
+
|
11
|
+
class_eval %(
|
12
|
+
alias intercepted_#{@original_with} #{@original_with}
|
13
|
+
|
14
|
+
def #{@original_with}(*expected_parameters, &matching_block)
|
15
|
+
synthesis_expectation.args = expected_parameters if synthesis_expectation
|
16
|
+
intercepted_#{@original_with}(*expected_parameters, &matching_block)
|
17
|
+
end
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Intercept the mock object framework's expectation method for declaring a mocked
|
22
|
+
# method's return values so that Synthesis can record them.
|
23
|
+
def intercept_expected_return_values_on(method_name)
|
24
|
+
@original_returns = method_name
|
25
|
+
|
26
|
+
class_eval %(
|
27
|
+
alias intercepted_#{@original_returns} #{@original_returns}
|
28
|
+
|
29
|
+
def #{@original_returns}(*values)
|
30
|
+
mock_expectation = intercepted_#{@original_returns}(*values)
|
31
|
+
synthesis_expectation.add_return_values(*values) if synthesis_expectation
|
32
|
+
mock_expectation
|
33
|
+
end
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Restore the original methods ExpectationInterceptor has rewritten and
|
38
|
+
# undefine their intercepted counterparts. Undefine the synthesis_expectation
|
39
|
+
# accessors.
|
40
|
+
def reset!
|
41
|
+
class_eval %(
|
42
|
+
alias #{@original_with} intercepted_#{@original_with}
|
43
|
+
alias #{@original_returns} intercepted_#{@original_returns}
|
44
|
+
undef intercepted_#{@original_with}
|
45
|
+
undef intercepted_#{@original_returns}
|
46
|
+
undef synthesis_expectation
|
47
|
+
undef synthesis_expectation=
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Classes extending ExpectationInterceptor will have a synthesis_expectation
|
52
|
+
# attribute accessor added to them.
|
53
|
+
def self.extended(receiver)
|
54
|
+
receiver.send(:attr_accessor, :synthesis_expectation)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Synthesis
|
2
|
+
class ExpectationMatcher
|
3
|
+
def initialize(expectation_one, expectation_two)
|
4
|
+
@expectation_one, @expectation_two = expectation_one, expectation_two
|
5
|
+
end
|
6
|
+
|
7
|
+
def match?
|
8
|
+
return false unless @expectation_one.class == @expectation_two.class
|
9
|
+
return false unless @expectation_one.receiver_class == @expectation_two.receiver_class
|
10
|
+
return false unless @expectation_one.method == @expectation_two.method
|
11
|
+
return false unless @expectation_one.arg_types == @expectation_two.arg_types
|
12
|
+
return false unless return_values_match?
|
13
|
+
true
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def return_values_match?
|
19
|
+
return true unless @expectation_one.return_values_defined? || @expectation_two.return_values_defined?
|
20
|
+
(@expectation_one.return_value_types & @expectation_two.return_value_types).any?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Synthesis
|
2
|
+
# Extend by the mock object framework's construct for declaring a
|
3
|
+
# mock object so that Synthesis can tap into it in order to record
|
4
|
+
# the expectation.
|
5
|
+
module ExpectationRecordEnabled
|
6
|
+
# Intercept the mock object framework's method for declaring a mock
|
7
|
+
# object so that Synthesis can record it.
|
8
|
+
def record_expectations_on(method_name)
|
9
|
+
@original_expects = method_name
|
10
|
+
|
11
|
+
class_eval %(
|
12
|
+
alias intercepted_#{@original_expects} #{@original_expects}
|
13
|
+
|
14
|
+
def #{@original_expects}(meth)
|
15
|
+
s_expectation = Synthesis::ExpectationRecord.add_expectation(self, meth, caller[0])
|
16
|
+
m_expectation = intercepted_#{@original_expects}(meth)
|
17
|
+
m_expectation.synthesis_expectation = s_expectation
|
18
|
+
m_expectation
|
19
|
+
end
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Restore the original methods ExpectationRecordEnabled has rewritten and
|
24
|
+
# undefine their intercepted counterparts.
|
25
|
+
def reset!
|
26
|
+
class_eval %(
|
27
|
+
alias #{@original_expects} intercepted_#{@original_expects}
|
28
|
+
undef intercepted_#{@original_expects}
|
29
|
+
)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Synthesis
|
2
2
|
class MethodInvocationWatcher
|
3
|
-
def self.invoked(receiver, method, args = [])
|
4
|
-
matcher = Expectation.new(receiver, method, nil, args)
|
3
|
+
def self.invoked(receiver, method, args = [], return_values = [])
|
4
|
+
matcher = Expectation.new(receiver, method, nil, args, return_values)
|
5
5
|
ExpectationRecord[matcher].invoked!
|
6
6
|
end
|
7
7
|
end
|
data/lib/synthesis/module.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
class Module
|
2
|
-
def expectation(method, track, args = [])
|
3
|
-
Synthesis::Expectation::Singleton.new(self, method, track, args)
|
2
|
+
def expectation(method, track, args = [], return_value = nil)
|
3
|
+
Synthesis::Expectation::Singleton.new(self, method, track, args, return_value)
|
4
4
|
end
|
5
5
|
end
|
data/lib/synthesis/object.rb
CHANGED
@@ -3,7 +3,7 @@ class Object
|
|
3
3
|
class << self; self end
|
4
4
|
end
|
5
5
|
|
6
|
-
def expectation(method, track, args = [])
|
7
|
-
Synthesis::Expectation::Instance.new(self, method, track, args)
|
6
|
+
def expectation(method, track, args = [], return_value = nil)
|
7
|
+
Synthesis::Expectation::Instance.new(self, method, track, args, return_value)
|
8
8
|
end
|
9
9
|
end
|
data/lib/synthesis/recordable.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
module Synthesis
|
2
2
|
module Recordable
|
3
3
|
def recordable_method(meth)
|
4
|
-
if method_defined?
|
5
|
-
defined_recordable_method
|
4
|
+
if method_defined?(meth)
|
5
|
+
defined_recordable_method(meth)
|
6
6
|
else
|
7
|
-
magic_recordable_method
|
7
|
+
magic_recordable_method(meth)
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
11
|
protected
|
12
12
|
|
13
13
|
def defined_recordable_method(meth)
|
14
|
-
unless method_defined?
|
14
|
+
unless method_defined?("__recordable__#{meth}".intern)
|
15
15
|
alias_method "__recordable__#{meth}".intern, meth
|
16
16
|
class_eval @@recordable_method_def[meth]
|
17
17
|
end
|
@@ -19,8 +19,9 @@ module Synthesis
|
|
19
19
|
|
20
20
|
@@recordable_method_def = proc { |meth| %(
|
21
21
|
def #{meth}(*args, &block)
|
22
|
-
|
23
|
-
|
22
|
+
return_value = send("__recordable__#{meth}", *args, &block)
|
23
|
+
MethodInvocationWatcher.invoked(self, "#{meth}".intern, args, [return_value])
|
24
|
+
return_value
|
24
25
|
end
|
25
26
|
)}
|
26
27
|
|
@@ -30,8 +31,9 @@ module Synthesis
|
|
30
31
|
|
31
32
|
@@recordable_magic_method_def = proc { |meth| %(
|
32
33
|
def #{meth}(*args)
|
33
|
-
|
34
|
-
|
34
|
+
return_value = method_missing(:#{meth}, *args)
|
35
|
+
MethodInvocationWatcher.invoked(self, "#{meth}".intern, args, [return_value])
|
36
|
+
return_value
|
35
37
|
end
|
36
38
|
|
37
39
|
def __recordable__#{meth}() raise "Don't ever call me" end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Synthesis
|
2
|
+
class Reporter
|
3
|
+
class << self
|
4
|
+
include Logging
|
5
|
+
|
6
|
+
def report
|
7
|
+
if failed?
|
8
|
+
ExpectationRecord.print_tested_expectations
|
9
|
+
ExpectationRecord.print_untested_expectations
|
10
|
+
ExpectationRecord.print_ignored
|
11
|
+
log; log "FAILED."
|
12
|
+
return -1
|
13
|
+
end
|
14
|
+
log; log "Verified #{ExpectationRecord.expectations.size} expectations"
|
15
|
+
log "SUCCESS."
|
16
|
+
0
|
17
|
+
end
|
18
|
+
|
19
|
+
def failed?
|
20
|
+
ExpectationRecord.untested_expectations.any?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/synthesis/runner.rb
CHANGED
data/lib/synthesis.rb
CHANGED
@@ -10,5 +10,8 @@ require "synthesis/module"
|
|
10
10
|
require "synthesis/expectation_record"
|
11
11
|
require "synthesis/method_invocation_watcher"
|
12
12
|
require "synthesis/expectation"
|
13
|
-
require "synthesis/
|
13
|
+
require "synthesis/expectation_matcher"
|
14
|
+
require "synthesis/expectation_interceptor"
|
15
|
+
require "synthesis/expectation_record_enabled"
|
16
|
+
require "synthesis/reporter"
|
14
17
|
require "synthesis/adapter"
|
@@ -2,17 +2,24 @@ require File.dirname(__FILE__) + "/helper"
|
|
2
2
|
|
3
3
|
class MochaExpectationTest < Test::Unit::TestCase
|
4
4
|
def setup
|
5
|
-
@mocha_expectation = Mocha::Expectation.new
|
5
|
+
@mocha_expectation = Mocha::Expectation.new(:mock, :blah)
|
6
|
+
@synthesis_expectation = Synthesis::Expectation.new(Hash, :method, :track, [], [1])
|
6
7
|
end
|
7
8
|
|
8
9
|
def test_holds_synthesis_expectation
|
9
|
-
@mocha_expectation.synthesis_expectation =
|
10
|
-
assert_equal
|
10
|
+
@mocha_expectation.synthesis_expectation = @synthesis_expectation
|
11
|
+
assert_equal(@synthesis_expectation, @mocha_expectation.synthesis_expectation)
|
11
12
|
end
|
12
13
|
|
13
14
|
def test_with_passes_expected_params_to_synthesis_expectation
|
14
|
-
@mocha_expectation.synthesis_expectation =
|
15
|
-
@mocha_expectation.with
|
15
|
+
@mocha_expectation.synthesis_expectation = @synthesis_expectation
|
16
|
+
@mocha_expectation.with(1, 2)
|
16
17
|
assert_equal([1, 2], @mocha_expectation.synthesis_expectation.args)
|
17
18
|
end
|
19
|
+
|
20
|
+
def test_passes_return_values_to_synthesis_expectation
|
21
|
+
@mocha_expectation.synthesis_expectation = @synthesis_expectation
|
22
|
+
@mocha_expectation.returns(:rock)
|
23
|
+
assert_equal([Fixnum, Symbol], @synthesis_expectation.return_value_types)
|
24
|
+
end
|
18
25
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/helper"
|
2
|
+
|
3
|
+
module Synthesis
|
4
|
+
class ExpectationMatcherTest < Test::Unit::TestCase
|
5
|
+
def test_match
|
6
|
+
exp1 = Expectation.new(Object.new, :foo, :track, [Array, Hash], [Array, Hash])
|
7
|
+
exp2 = Expectation.new(Object.new, :foo, :track, [Array, Hash], [Array, Hash])
|
8
|
+
assert(ExpectationMatcher.new(exp1, exp2).match?)
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_no_match_based_on_expectation_type
|
12
|
+
exp1 = Expectation.new(Array, :foo, :track, [Array, Hash], [Array, Hash])
|
13
|
+
exp2 = Expectation.new(Array.new, :foo, :track, [Array, Hash], [Array, Hash])
|
14
|
+
assert(!ExpectationMatcher.new(exp1, exp2).match?)
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_no_match_based_on_receiver_type
|
18
|
+
exp1 = Expectation.new(Array.new, :foo, :track, [Array, Hash], [Array, Hash])
|
19
|
+
exp2 = Expectation.new(Hash.new, :foo, :track, [Array, Hash], [Array, Hash])
|
20
|
+
assert(!ExpectationMatcher.new(exp1, exp2).match?)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_no_match_based_on_method_name
|
24
|
+
exp1 = Expectation.new(Object.new, :foo, :track, [Array, Hash], [Array, Hash])
|
25
|
+
exp2 = Expectation.new(Object.new, :bar, :track, [Array, Hash], [Array, Hash])
|
26
|
+
assert(!ExpectationMatcher.new(exp1, exp2).match?)
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_no_match_based_on_arg_types
|
30
|
+
exp1 = Expectation.new(Object.new, :foo, :track, [1, "a"], [Array, Hash])
|
31
|
+
exp2 = Expectation.new(Object.new, :foo, :track, [1, 2], [Array, Hash])
|
32
|
+
assert(!ExpectationMatcher.new(exp1, exp2).match?)
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_no_match_based_on_return_value_types
|
36
|
+
exp1 = Expectation.new(Object.new, :foo, :track, [Array, Hash])
|
37
|
+
exp1.add_return_values(:sym)
|
38
|
+
exp2 = Expectation.new(Object.new, :foo, :track, [Array, Hash], ["string"])
|
39
|
+
assert(!ExpectationMatcher.new(exp1, exp2).match?)
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_match_based_on_intersection_of_return_value_types
|
43
|
+
exp1 = Expectation.new(Object.new, :foo, :track, [Array, Hash], [[1], {:a => :b}])
|
44
|
+
exp2 = Expectation.new(Object.new, :foo, :track, [Array, Hash], [[4], {:a => 9}])
|
45
|
+
assert(ExpectationMatcher.new(exp1, exp2).match?)
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_ignores_return_value_types_unless_return_values_defined
|
49
|
+
exp1 = Expectation.new(Object.new, :foo, :track, [Array, Hash])
|
50
|
+
exp2 = Expectation.new(Object.new, :foo, :track, [Array, Hash])
|
51
|
+
assert(ExpectationMatcher.new(exp1, exp2).match?)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -14,17 +14,17 @@ module Synthesis
|
|
14
14
|
|
15
15
|
def test_watches_expectation_invocations
|
16
16
|
c = Class.new { def foo; end }
|
17
|
-
ExpectationRecord.add_expectation
|
17
|
+
ExpectationRecord.add_expectation(c.new, :foo, :track)
|
18
18
|
ExpectationRecord.record_invocations
|
19
19
|
a = c.new
|
20
|
-
MethodInvocationWatcher.expects(:invoked).with(a, :foo, [])
|
20
|
+
MethodInvocationWatcher.expects(:invoked).with(a, :foo, [], [nil])
|
21
21
|
a.foo
|
22
22
|
end
|
23
23
|
|
24
24
|
def test_finds_expectation
|
25
|
-
ExpectationRecord.add_expectation
|
26
|
-
expected
|
27
|
-
matcher = Expectation.new
|
25
|
+
expected = ExpectationRecord.add_expectation(Object.new, :foo, :track)
|
26
|
+
expected.add_return_values(20)
|
27
|
+
matcher = Expectation.new(Object.new, :foo, :track, [], [20])
|
28
28
|
actual = ExpectationRecord[matcher]
|
29
29
|
assert_equal(expected, actual)
|
30
30
|
end
|
@@ -37,15 +37,16 @@ module Synthesis
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def test_does_not_add_expectation_for_ignored_class
|
40
|
-
ExpectationRecord.ignore
|
41
|
-
ExpectationRecord.add_expectation
|
40
|
+
ExpectationRecord.ignore(Hash)
|
41
|
+
ExpectationRecord.add_expectation(Hash, :foo, :track)
|
42
42
|
assert ExpectationRecord.expectations.empty?
|
43
43
|
end
|
44
44
|
|
45
45
|
def test_returns_added_expectation_on_add
|
46
|
-
expected = Expectation.new
|
47
|
-
actual = ExpectationRecord.add_expectation
|
48
|
-
|
46
|
+
expected = Expectation.new(Hash, :foo, :track, [], [:return_val])
|
47
|
+
actual = ExpectationRecord.add_expectation(Hash, :foo, :track)
|
48
|
+
actual.add_return_values(:return_val)
|
49
|
+
assert_equal(expected, actual)
|
49
50
|
end
|
50
51
|
end
|
51
52
|
end
|
@@ -3,77 +3,76 @@ require File.dirname(__FILE__) + "/helper"
|
|
3
3
|
module Synthesis
|
4
4
|
class ExpectationTest < Test::Unit::TestCase
|
5
5
|
def test_creates_singleton_method_expectation_given_class
|
6
|
-
expectation = Expectation.new
|
7
|
-
assert_kind_of
|
6
|
+
expectation = Expectation.new(Class.new, :foo, :track)
|
7
|
+
assert_kind_of(Expectation::Singleton, expectation)
|
8
8
|
end
|
9
9
|
|
10
10
|
def test_creates_instance_method_expectation_given_instance
|
11
|
-
expectation = Expectation.new
|
12
|
-
assert_kind_of
|
11
|
+
expectation = Expectation.new(Class.new.new, :foo, :track)
|
12
|
+
assert_kind_of(Expectation::Instance, expectation)
|
13
13
|
end
|
14
14
|
|
15
15
|
def test_creates_singleton_method_expectation_given_module
|
16
|
-
expectation = Expectation.new
|
17
|
-
assert_kind_of
|
16
|
+
expectation = Expectation.new(Module.new, :foo, :track)
|
17
|
+
assert_kind_of(Expectation::Singleton, expectation)
|
18
18
|
end
|
19
19
|
|
20
20
|
def test_records_instance_receiver_method_invocations
|
21
21
|
receiver = Class.new { def foo;end }
|
22
|
-
expectation = Expectation.new
|
22
|
+
expectation = Expectation.new(receiver.new, :foo, :track)
|
23
23
|
expectation.record_invocations
|
24
|
-
assert_respond_to
|
24
|
+
assert_respond_to(receiver.new, :__recordable__foo)
|
25
25
|
end
|
26
26
|
|
27
27
|
def test_records_singleton_receiver_method_invocations
|
28
28
|
receiver = Class.new { def self.foo;end }
|
29
|
-
expectation = Expectation.new
|
29
|
+
expectation = Expectation.new(receiver, :foo, :track, [], [:return])
|
30
30
|
expectation.record_invocations
|
31
|
-
assert_respond_to
|
31
|
+
assert_respond_to(receiver, :__recordable__foo)
|
32
32
|
end
|
33
|
-
|
34
|
-
def
|
35
|
-
|
36
|
-
|
37
|
-
|
33
|
+
|
34
|
+
def test_make_sure_equality_works_with_uniq
|
35
|
+
expectations = [
|
36
|
+
Expectation.new(String, :new, [], [:arg1, :arg2], [:return]),
|
37
|
+
Expectation.new(String, :new, [], [:arg1, :arg2], [:return])
|
38
|
+
]
|
39
|
+
assert_equal(1, expectations.uniq.size)
|
38
40
|
end
|
39
|
-
|
40
|
-
def
|
41
|
-
|
42
|
-
exp2 = Expectation.new Array.new, :foo, :track
|
43
|
-
assert_not_equal exp1, exp2
|
41
|
+
|
42
|
+
def test_singleton_receiver_class
|
43
|
+
assert_equal(String, Expectation.new(String, :new, :track, [], []).receiver_class)
|
44
44
|
end
|
45
45
|
|
46
|
-
def
|
47
|
-
|
48
|
-
exp2 = Expectation.new Object.new, :foo, :track, [1, 2]
|
49
|
-
assert_equal exp1, exp2
|
46
|
+
def test_instance_receiver_class
|
47
|
+
assert_equal(String, Expectation.new(String.new, :new, :track, [], []).receiver_class)
|
50
48
|
end
|
51
49
|
|
52
|
-
def
|
53
|
-
|
54
|
-
|
55
|
-
assert_not_equal exp1, exp2
|
50
|
+
def test_arg_types
|
51
|
+
expectation = Expectation.new(String.new, :new, :track, [:sym, "str"], [])
|
52
|
+
assert_equal([Symbol, String], expectation.arg_types)
|
56
53
|
end
|
57
|
-
|
58
|
-
def
|
59
|
-
|
60
|
-
|
61
|
-
Expectation.new(String, :new, [])
|
62
|
-
]
|
63
|
-
assert_equal(1, arr.uniq.size)
|
54
|
+
|
55
|
+
def test_return_value_types
|
56
|
+
expectation = Expectation.new(String.new, :new, :track, [], [:sym, "str"])
|
57
|
+
assert_equal([Symbol, String], expectation.return_value_types)
|
64
58
|
end
|
65
|
-
|
66
|
-
def
|
67
|
-
expectation = Expectation.new
|
68
|
-
|
69
|
-
|
70
|
-
|
59
|
+
|
60
|
+
def test_adds_return_value
|
61
|
+
expectation = Expectation.new(String.new, :new, :track, [], [:sym])
|
62
|
+
expectation.add_return_values("str")
|
63
|
+
actual = expectation.instance_variable_get(:@return_values)
|
64
|
+
assert_equal([:sym, "str"], actual)
|
71
65
|
end
|
72
|
-
|
73
|
-
def
|
74
|
-
|
75
|
-
|
76
|
-
|
66
|
+
|
67
|
+
def test_return_values_defined_when_return_values_added
|
68
|
+
expectation = Expectation.new(String.new, :new, :track, [], [:sym])
|
69
|
+
expectation.add_return_values("str")
|
70
|
+
assert(expectation.return_values_defined?)
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_return_values_not_defined_when_no_return_values_added
|
74
|
+
expectation = Expectation.new(String.new, :new, :track, [], [:sym])
|
75
|
+
assert(!expectation.return_values_defined?)
|
77
76
|
end
|
78
77
|
end
|
79
78
|
end
|
@@ -3,8 +3,8 @@ require File.dirname(__FILE__) + "/helper"
|
|
3
3
|
module Synthesis
|
4
4
|
class MethodInvocationWatcherTest < Test::Unit::TestCase
|
5
5
|
def test_marks_expectation_invoked
|
6
|
-
ExpectationRecord.add_expectation(Hash, :to_s, [])
|
7
|
-
MethodInvocationWatcher.invoked(Hash, :to_s, [])
|
6
|
+
ExpectationRecord.add_expectation(Hash, :to_s, :track, []).add_return_values(1)
|
7
|
+
MethodInvocationWatcher.invoked(Hash, :to_s, [], [1])
|
8
8
|
expectation = ExpectationRecord.expectations.to_a.first
|
9
9
|
assert expectation.invoked?
|
10
10
|
end
|
@@ -14,7 +14,7 @@ module Synthesis
|
|
14
14
|
foo.extend Recordable
|
15
15
|
foo.recordable_method(:b)
|
16
16
|
bar = foo.new
|
17
|
-
MethodInvocationWatcher.expects(:invoked).with(bar, :b, [])
|
17
|
+
MethodInvocationWatcher.expects(:invoked).with(bar, :b, [], [nil])
|
18
18
|
bar.b
|
19
19
|
end
|
20
20
|
|
@@ -59,7 +59,7 @@ module Synthesis
|
|
59
59
|
foo.extend Recordable
|
60
60
|
foo.recordable_method(:b)
|
61
61
|
bar = foo.new
|
62
|
-
MethodInvocationWatcher.expects(:invoked).with(bar, :b, [])
|
62
|
+
MethodInvocationWatcher.expects(:invoked).with(bar, :b, [], [:magic!])
|
63
63
|
bar.b
|
64
64
|
end
|
65
65
|
end
|
@@ -5,7 +5,7 @@ class DataBranderTest < Test::Unit::TestCase
|
|
5
5
|
def test_saves_branded_to_pstore
|
6
6
|
pstore = Storage.new 'whatever'
|
7
7
|
pstore.expects(:save).with('METAL - rock')
|
8
|
-
DataBrander.new(pstore).save_branded
|
8
|
+
DataBrander.new(pstore).save_branded('rock')
|
9
9
|
end
|
10
10
|
|
11
11
|
def test_ignores_total_ducks
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: synthesis
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stuart Caborn, George Malamidis
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-04-13 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -29,13 +29,16 @@ files:
|
|
29
29
|
- lib/synthesis/adapter.rb
|
30
30
|
- lib/synthesis/class.rb
|
31
31
|
- lib/synthesis/expectation.rb
|
32
|
+
- lib/synthesis/expectation_interceptor.rb
|
33
|
+
- lib/synthesis/expectation_matcher.rb
|
32
34
|
- lib/synthesis/expectation_record.rb
|
35
|
+
- lib/synthesis/expectation_record_enabled.rb
|
33
36
|
- lib/synthesis/logging.rb
|
34
37
|
- lib/synthesis/method_invocation_watcher.rb
|
35
38
|
- lib/synthesis/module.rb
|
36
39
|
- lib/synthesis/object.rb
|
37
40
|
- lib/synthesis/recordable.rb
|
38
|
-
- lib/synthesis/
|
41
|
+
- lib/synthesis/reporter.rb
|
39
42
|
- lib/synthesis/runner.rb
|
40
43
|
- lib/synthesis/task.rb
|
41
44
|
- lib/synthesis.rb
|
@@ -49,6 +52,7 @@ files:
|
|
49
52
|
- test/synthesis/adapter/rspec/message_expectation_test.rb
|
50
53
|
- test/synthesis/adapter/rspec/methods_test.rb
|
51
54
|
- test/synthesis/class_test.rb
|
55
|
+
- test/synthesis/expectation_matcher_test.rb
|
52
56
|
- test/synthesis/expectation_record_test.rb
|
53
57
|
- test/synthesis/expectation_test.rb
|
54
58
|
- test/synthesis/helper.rb
|
@@ -95,7 +99,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
95
99
|
requirements: []
|
96
100
|
|
97
101
|
rubyforge_project: synthesis
|
98
|
-
rubygems_version: 1.
|
102
|
+
rubygems_version: 1.1.1
|
99
103
|
signing_key:
|
100
104
|
specification_version: 2
|
101
105
|
summary: A tool for Synthesized Testing
|
data/lib/synthesis/report.rb
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
module Synthesis
|
2
|
-
extend Logging
|
3
|
-
|
4
|
-
def report!
|
5
|
-
if failed?
|
6
|
-
ExpectationRecord.print_tested_expectations
|
7
|
-
ExpectationRecord.print_untested_expectations
|
8
|
-
ExpectationRecord.print_ignored
|
9
|
-
log; log "FAILED."
|
10
|
-
return -1
|
11
|
-
end
|
12
|
-
log; log "Verified #{ExpectationRecord.expectations.size} expectations"
|
13
|
-
log "SUCCESS."
|
14
|
-
0
|
15
|
-
end
|
16
|
-
|
17
|
-
def failed?
|
18
|
-
ExpectationRecord.untested_expectations.any?
|
19
|
-
end
|
20
|
-
|
21
|
-
module_function :report!, :failed?
|
22
|
-
end
|