motion-spec 0.4.2 → 0.5.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 +8 -8
- data/LICENSE +3 -1
- data/README.md +13 -0
- data/Rakefile +1 -1
- data/app/app_delegate.rb +3 -1
- data/lib/motion-spec/context.rb +17 -1
- data/lib/motion-spec/context_helper/memoized_helpers.rb +1 -1
- data/lib/motion-spec/core.rb +4 -4
- data/lib/motion-spec/expectation.rb +1 -1
- data/lib/motion-spec/extensions/boolean.rb +13 -4
- data/lib/motion-spec/extensions/numeric.rb +3 -1
- data/lib/motion-spec/extensions/proc.rb +2 -2
- data/lib/motion-spec/matcher/be_false.rb +1 -1
- data/lib/motion-spec/matcher/be_nil.rb +2 -2
- data/lib/motion-spec/matcher/be_true.rb +1 -1
- data/lib/motion-spec/matcher/be_within.rb +2 -2
- data/lib/motion-spec/matcher/change.rb +3 -3
- data/lib/motion-spec/matcher/end_with.rb +1 -1
- data/lib/motion-spec/matcher/have_generic.rb +1 -1
- data/lib/motion-spec/matcher/have_items.rb +1 -1
- data/lib/motion-spec/matcher/include.rb +1 -1
- data/lib/motion-spec/matcher/match_array.rb +1 -1
- data/lib/motion-spec/matcher/raise_error.rb +9 -9
- data/lib/motion-spec/matcher/respond_to.rb +1 -1
- data/lib/motion-spec/matcher/satisfy.rb +2 -2
- data/lib/motion-spec/matcher/single_method.rb +1 -1
- data/lib/motion-spec/matcher/start_with.rb +1 -1
- data/lib/motion-spec/mock/method_bind_unbind.rb +57 -0
- data/lib/motion-spec/mock/mock.rb +63 -0
- data/lib/motion-spec/mock/mocks.rb +29 -0
- data/lib/motion-spec/mock/proxy.rb +115 -0
- data/lib/motion-spec/mock/stub.rb +37 -0
- data/lib/motion-spec/mock/stubs.rb +18 -0
- data/lib/motion-spec/output/ruby_mine.rb +2 -2
- data/lib/motion-spec/should.rb +2 -2
- data/lib/motion-spec/specification.rb +13 -8
- data/lib/motion-spec/version.rb +1 -1
- data/lib/motion-spec.rb +8 -5
- metadata +9 -3
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NGM3ZTc2NzgwMWRhMjAyNDg1OTgxNTYyNDk5YjAwNjk5MDEzNTA1ZQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NDQwODRjNDY5ZGNjZmExYzExMThmM2Y2MjdlZTMwYTgyZTdhMDkzYg==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MGY3NDZkZTgxNjkxYzBkMzUyMjA1MmI1ODcxYzlmNTI1YTA4MDhhYjYzMjIx
|
10
|
+
MTUzZjMxZGU5MDZmNmUzM2EyMTYzNzBhMDQxYTE5ODliNDQ0OWEyZjZjNjFk
|
11
|
+
YmE1MmE4YjA5OTU3MWUwNzIzYmY2ZGIyMjk5NDNjNWMwZWJiMjc=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
N2VhMDg2NjZmNGYyNDU4NmI4ODg1YjMwNGY4MGNkZGVlN2MyNzViYTAxODJl
|
14
|
+
OTUwYTRhMzU3ZjRhODY3NGIwYmY1MGRlMTNkMDAxZWEzZjU4OWVkZWM0YjUx
|
15
|
+
MjM2ODk5NDBmMjQ0MWRhYTFhNjJlYjA5NWMxYjlmMjNiNDg5NmU=
|
data/LICENSE
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
The MIT License (MIT)
|
2
2
|
|
3
3
|
Copyright (c) 2007, 2008 Christian Neukirchen <purl.org/net/chneukirchen>
|
4
|
+
Copyright (c) 2008 Jeremy McAnally
|
5
|
+
Copyright (c) 2012 Francis Chong
|
4
6
|
Copyright (c) 2011 Eloy Durán <eloy.de.enige@gmail.com>
|
5
|
-
Copyright (c) 2015 Jonathan Bender <jlbender@gmail.com>
|
7
|
+
Copyright (c) 2015, 2016 Jonathan Bender <jlbender@gmail.com>
|
6
8
|
|
7
9
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
10
|
of this software and associated documentation files (the "Software"), to deal
|
data/README.md
CHANGED
@@ -53,9 +53,22 @@ describe AwesomeClass do
|
|
53
53
|
|
54
54
|
it { is_expected.to have_foo('bar') }
|
55
55
|
end
|
56
|
+
|
57
|
+
context 'stubbing a method' do
|
58
|
+
before { subject.stub!(:awesome_method, return: 'awesomeness') }
|
59
|
+
|
60
|
+
subject { AwesomeClass.new }
|
61
|
+
|
62
|
+
it { expect(subject.awesome_method).to eq 'awesomeness' }
|
63
|
+
end
|
56
64
|
end
|
57
65
|
```
|
58
66
|
|
67
|
+
### `mock!` vs `stub!`
|
68
|
+
|
69
|
+
`mock!` ensures that the method is called (and removes the implementation when
|
70
|
+
it is), while `stub!` simply replaces the method for the duration of the spec.
|
71
|
+
|
59
72
|
## Contributing
|
60
73
|
|
61
74
|
Bug reports and pull requests are welcome on GitHub at
|
data/Rakefile
CHANGED
data/app/app_delegate.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
2
|
class AppDelegate
|
3
|
-
|
3
|
+
# rubocop:disable Lint/UnusedMethodArgument
|
4
|
+
def application(_application, didFinishLaunchingWithOptions:launchOptions)
|
4
5
|
true
|
5
6
|
end
|
7
|
+
# rubocop:enable Lint/UnusedMethodArgument
|
6
8
|
end
|
data/lib/motion-spec/context.rb
CHANGED
@@ -38,7 +38,7 @@ module MotionSpec
|
|
38
38
|
@specifications[@current_specification_index]
|
39
39
|
end
|
40
40
|
|
41
|
-
def specification_did_finish(
|
41
|
+
def specification_did_finish(_spec)
|
42
42
|
return if Platform.android?
|
43
43
|
|
44
44
|
if (@current_specification_index + 1) < @specifications.size
|
@@ -76,6 +76,8 @@ module MotionSpec
|
|
76
76
|
|
77
77
|
Counter[:specifications] += 1
|
78
78
|
|
79
|
+
@after << proc { verify_mocks_were_called }
|
80
|
+
|
79
81
|
@specifications << Specification.new(
|
80
82
|
self, description, block, @before, @after
|
81
83
|
)
|
@@ -156,5 +158,19 @@ module MotionSpec
|
|
156
158
|
end
|
157
159
|
end
|
158
160
|
end
|
161
|
+
|
162
|
+
def mock_failures
|
163
|
+
MotionSpec::Mocks.failures
|
164
|
+
end
|
165
|
+
|
166
|
+
def verify_mocks_were_called
|
167
|
+
return unless mock_failures && !mock_failures.empty?
|
168
|
+
|
169
|
+
fails = mock_failures.map do |object, method|
|
170
|
+
"#{object.inspect} expected #{method}"
|
171
|
+
end
|
172
|
+
|
173
|
+
should.flunk "Unmet expectations: #{fails.join(', ')}"
|
174
|
+
end
|
159
175
|
end
|
160
176
|
end
|
@@ -5,7 +5,7 @@ module MotionSpec
|
|
5
5
|
attr_accessor :__memoized
|
6
6
|
|
7
7
|
def let(name, &block)
|
8
|
-
|
8
|
+
fail '#let or #subject called without a block' unless block_given?
|
9
9
|
|
10
10
|
(class << self; self; end).class_eval do
|
11
11
|
define_method(name) do
|
data/lib/motion-spec/core.rb
CHANGED
@@ -7,9 +7,9 @@ module MotionSpec
|
|
7
7
|
|
8
8
|
Counter = Hash.new(0)
|
9
9
|
ErrorLog = ''
|
10
|
-
Shared = Hash.new
|
11
|
-
|
12
|
-
|
10
|
+
Shared = Hash.new do |_, name|
|
11
|
+
fail NameError, "no such context: #{name.inspect}"
|
12
|
+
end
|
13
13
|
|
14
14
|
RestrictName = // unless defined? RestrictName
|
15
15
|
RestrictContext = // unless defined? RestrictContext
|
@@ -74,7 +74,7 @@ module MotionSpec
|
|
74
74
|
@main_activity
|
75
75
|
end
|
76
76
|
|
77
|
-
def self.context_did_finish(
|
77
|
+
def self.context_did_finish(_context)
|
78
78
|
return if Platform.android?
|
79
79
|
|
80
80
|
handle_specification_end
|
@@ -1,13 +1,22 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
2
|
class Object
|
3
|
-
def true
|
4
|
-
|
3
|
+
def true?
|
4
|
+
false
|
5
|
+
end
|
6
|
+
|
7
|
+
def false?
|
8
|
+
false
|
9
|
+
end
|
5
10
|
end
|
6
11
|
|
7
12
|
class TrueClass
|
8
|
-
def true
|
13
|
+
def true?
|
14
|
+
true
|
15
|
+
end
|
9
16
|
end
|
10
17
|
|
11
18
|
class FalseClass
|
12
|
-
def false
|
19
|
+
def false?
|
20
|
+
true
|
21
|
+
end
|
13
22
|
end
|
@@ -3,11 +3,11 @@ module MotionSpec
|
|
3
3
|
module Matcher
|
4
4
|
class BeNil
|
5
5
|
def matches?(value)
|
6
|
-
value
|
6
|
+
value.nil?
|
7
7
|
end
|
8
8
|
|
9
9
|
def fail!(subject, negated)
|
10
|
-
|
10
|
+
fail FailedExpectation.new(
|
11
11
|
FailMessageRenderer.message_for_be_nil(negated, subject)
|
12
12
|
)
|
13
13
|
end
|
@@ -14,12 +14,12 @@ module MotionSpec
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def matches?(subject)
|
17
|
-
|
17
|
+
fail InvalidMatcher.new(INVALID_MATCH_ERROR) unless @center_value
|
18
18
|
(subject - @center_value).abs <= @range
|
19
19
|
end
|
20
20
|
|
21
21
|
def fail!(subject, negated)
|
22
|
-
|
22
|
+
fail FailedExpectation.new(
|
23
23
|
FailMessageRenderer.message_for_be_within(
|
24
24
|
negated, subject, @range, @center_value
|
25
25
|
)
|
@@ -11,7 +11,7 @@ module MotionSpec
|
|
11
11
|
self
|
12
12
|
end
|
13
13
|
|
14
|
-
def matches?(
|
14
|
+
def matches?(_subject, &expectation_block)
|
15
15
|
old_value = @change_block.call
|
16
16
|
expectation_block.call
|
17
17
|
new_value = @change_block.call
|
@@ -23,8 +23,8 @@ module MotionSpec
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
def fail!(
|
27
|
-
|
26
|
+
def fail!(_subject, negated)
|
27
|
+
fail FailedExpectation.new(
|
28
28
|
FailMessageRenderer.message_for_change(
|
29
29
|
negated, @change_amount, @value_diff
|
30
30
|
)
|
@@ -7,7 +7,7 @@ module MotionSpec
|
|
7
7
|
@error_message = (error_class.is_a?(String) || error_class.is_a?(Regexp)) ? error_class : message
|
8
8
|
end
|
9
9
|
|
10
|
-
def matches?(
|
10
|
+
def matches?(_value, &block)
|
11
11
|
block.call
|
12
12
|
false
|
13
13
|
rescue Exception => e
|
@@ -19,12 +19,12 @@ module MotionSpec
|
|
19
19
|
return false unless exception.is_a?(@error_class)
|
20
20
|
|
21
21
|
is_match = case @error_message
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
22
|
+
when String
|
23
|
+
exception.message.include?(@error_message)
|
24
|
+
when Regexp
|
25
|
+
@error_message.match(exception.message)
|
26
|
+
else
|
27
|
+
false
|
28
28
|
end
|
29
29
|
|
30
30
|
return false unless is_match
|
@@ -32,10 +32,10 @@ module MotionSpec
|
|
32
32
|
true
|
33
33
|
end
|
34
34
|
|
35
|
-
def fail!(
|
35
|
+
def fail!(_subject, negated)
|
36
36
|
show_class = @error_class != Exception
|
37
37
|
show_message = !@error_message.is_a?(String) || !@error_message.empty?
|
38
|
-
|
38
|
+
fail FailedExpectation.new(
|
39
39
|
FailMessageRenderer.message_for_raise_error(
|
40
40
|
negated, show_class, @error_class, show_message, @error_message,
|
41
41
|
@rescued_exception
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
# The meta-programming that allows us to pop methods on and off for mocking
|
3
|
+
class Object
|
4
|
+
# The hidden singleton lurks behind everyone
|
5
|
+
def metaclass
|
6
|
+
class << self
|
7
|
+
self
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def meta_eval(&block)
|
12
|
+
metaclass.instance_eval(&block)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Adds methods to a metaclass
|
16
|
+
def meta_def(name, &method_body)
|
17
|
+
meta_eval do
|
18
|
+
define_method(name) { |*args, &block| method_body.call(*args, &block) }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def safe_meta_def(name, &method_body)
|
23
|
+
metaclass.remember_original_method(name)
|
24
|
+
meta_def(name, &method_body)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Defines an instance method within a class
|
28
|
+
def class_def(name, &block)
|
29
|
+
class_eval { define_method name, &block }
|
30
|
+
end
|
31
|
+
|
32
|
+
def reset(method_name)
|
33
|
+
metaclass.restore_original_method(method_name)
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
def motion_spec_original_method_name(method_name)
|
39
|
+
"__original_#{method_name}".to_sym
|
40
|
+
end
|
41
|
+
|
42
|
+
def remember_original_method(method_name)
|
43
|
+
if method_defined?(method_name)
|
44
|
+
alias_method motion_spec_original_method_name(method_name), method_name
|
45
|
+
end
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
def restore_original_method(method_name)
|
50
|
+
original_method_name = motion_spec_original_method_name(method_name)
|
51
|
+
if method_defined?(original_method_name)
|
52
|
+
alias_method method_name, original_method_name
|
53
|
+
remove_method original_method_name
|
54
|
+
end
|
55
|
+
self
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
class Object
|
3
|
+
# Create a mock method on an object. A mock object will place an expectation
|
4
|
+
# on behavior and cause a test failure if it's not fulfilled.
|
5
|
+
#
|
6
|
+
# ==== Examples
|
7
|
+
#
|
8
|
+
# my_string = "a wooden rabbit"
|
9
|
+
# my_string.mock!(:retreat!, :return => "run away! run away!")
|
10
|
+
# my_string.mock!(:question, :return => "what is the airspeed velocity of an unladen sparrow?")
|
11
|
+
#
|
12
|
+
# # test/your_test.rb
|
13
|
+
# my_string.retreat! # => "run away! run away!"
|
14
|
+
# # If we let the test case end at this point, it fails with:
|
15
|
+
# # Unmet expectation: #<Sparrow:1ee7> expected question
|
16
|
+
#
|
17
|
+
def mock!(method, options = {}, &block)
|
18
|
+
MotionSpec::Mocks.add([self, method])
|
19
|
+
|
20
|
+
behavior =
|
21
|
+
if block_given?
|
22
|
+
lambda do |*args|
|
23
|
+
fail ArgumentError if block.arity >= 0 && args.length != block.arity
|
24
|
+
|
25
|
+
MotionSpec::Mocks.verify([self, method])
|
26
|
+
block.call(*args)
|
27
|
+
end
|
28
|
+
elsif !options[:yield].nil?
|
29
|
+
lambda do |*_args|
|
30
|
+
MotionSpec::Mocks.verify([self, method])
|
31
|
+
yield(options[:yield])
|
32
|
+
end
|
33
|
+
else
|
34
|
+
lambda do |*_args|
|
35
|
+
MotionSpec::Mocks.verify([self, method])
|
36
|
+
return options[:return]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
safe_meta_def method, &behavior
|
41
|
+
end
|
42
|
+
|
43
|
+
def should_not_call(method)
|
44
|
+
safe_meta_def(method) do
|
45
|
+
should.flunk "Umet expectations: #{method} expected to not be called"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
module Kernel
|
51
|
+
# Create a pure mock object rather than mocking specific methods on an object.
|
52
|
+
#
|
53
|
+
# ==== Examples
|
54
|
+
#
|
55
|
+
# my_mock = mock(:thing, :return => "whee!")
|
56
|
+
# my_mock.thing # => "whee"
|
57
|
+
#
|
58
|
+
def mock(method, options = {}, &block)
|
59
|
+
mock_object = Object.new
|
60
|
+
mock_object.mock!(method, options, &block)
|
61
|
+
mock_object
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module MotionSpec
|
3
|
+
# A class to track the mocks and proxies that have been satisfied
|
4
|
+
class Mocks
|
5
|
+
class <<self
|
6
|
+
def size
|
7
|
+
@mocks ? 0 : @mocks.size
|
8
|
+
end
|
9
|
+
|
10
|
+
def add(mock)
|
11
|
+
@mocks ||= []
|
12
|
+
Counter[:requirements] += 1
|
13
|
+
@mocks << mock
|
14
|
+
end
|
15
|
+
|
16
|
+
def verify(mock)
|
17
|
+
@mocks.delete(mock)
|
18
|
+
end
|
19
|
+
|
20
|
+
def failures
|
21
|
+
@mocks
|
22
|
+
end
|
23
|
+
|
24
|
+
def clear!
|
25
|
+
@mocks = []
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
class Object
|
3
|
+
# Creates a proxy method on an object. In this setup, it places an expectation on an object (like a mock)
|
4
|
+
# but still calls the original method. So if you want to make sure the method is called and still return
|
5
|
+
# its value, or simply want to invoke the side effects of a method and return a stubbed value, then you can
|
6
|
+
# do that.
|
7
|
+
#
|
8
|
+
# ==== Examples
|
9
|
+
#
|
10
|
+
# class Parrot
|
11
|
+
# def speak!
|
12
|
+
# puts @words
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# def say_this(words)
|
16
|
+
# @words = words
|
17
|
+
# "I shall say #{words}!"
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# # => test/your_test.rb
|
22
|
+
# sqawky = Parrot.new
|
23
|
+
# sqawky.proxy!(:say_this)
|
24
|
+
# # Proxy method still calls original method...
|
25
|
+
# sqawky.say_this("hey") # => "I shall say hey!"
|
26
|
+
# sqawky.speak! # => "hey"
|
27
|
+
#
|
28
|
+
# sqawky.proxy!(:say_this, "herro!")
|
29
|
+
# # Even though we return a stubbed value...
|
30
|
+
# sqawky.say_this("these words") # => "herro!"
|
31
|
+
# # ...the side effects are still there.
|
32
|
+
# sqawky.speak! # => "these words"
|
33
|
+
#
|
34
|
+
# TODO: This implementation is still very rough. Needs refactoring and refining. Won't
|
35
|
+
# work on ActiveRecord attributes, for example.
|
36
|
+
#
|
37
|
+
def proxy!(method, options = {}, &block)
|
38
|
+
MotionSpec::Mocks.add([self, method])
|
39
|
+
|
40
|
+
if respond_to?(method)
|
41
|
+
proxy_existing_method(method, options, &block)
|
42
|
+
else
|
43
|
+
proxy_missing_method(method, options, &block)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
protected
|
48
|
+
|
49
|
+
def proxy_existing_method(method, options = {}, &_block)
|
50
|
+
method_alias = "__old_#{method}".to_sym
|
51
|
+
|
52
|
+
meta_eval { module_eval { alias_method method_alias, method } }
|
53
|
+
|
54
|
+
check_arity = proc do |args|
|
55
|
+
arity = method(method_alias.to_sym).arity
|
56
|
+
|
57
|
+
# Negative arity means some params are optional, so we check for the
|
58
|
+
# minimum required. Sadly, we can't tell what the maximum is.
|
59
|
+
has_mimimum_arguments =
|
60
|
+
if arity >= 0
|
61
|
+
args.length != arity
|
62
|
+
else
|
63
|
+
args.length < ~arity
|
64
|
+
end
|
65
|
+
|
66
|
+
fail ArgumentError unless has_mimimum_arguments
|
67
|
+
end
|
68
|
+
|
69
|
+
default_operation =
|
70
|
+
lambda do |*args|
|
71
|
+
check_arity.call(args)
|
72
|
+
|
73
|
+
MotionSpec::Mocks.verify([self, method])
|
74
|
+
|
75
|
+
if method(method_alias.to_sym).arity == 0
|
76
|
+
send(method_alias)
|
77
|
+
else
|
78
|
+
send(method_alias, *args)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
behavior =
|
83
|
+
if options[:return]
|
84
|
+
lambda do |*args|
|
85
|
+
default_operation.call(*args)
|
86
|
+
return options[:return]
|
87
|
+
end
|
88
|
+
else
|
89
|
+
default_operation
|
90
|
+
end
|
91
|
+
|
92
|
+
meta_def method, &behavior
|
93
|
+
end
|
94
|
+
|
95
|
+
def proxy_missing_method(method, options = {}, &_block)
|
96
|
+
default_operation =
|
97
|
+
lambda do |*args|
|
98
|
+
MotionSpec::Mocks.verify([self, method])
|
99
|
+
|
100
|
+
method_missing(method, args)
|
101
|
+
end
|
102
|
+
|
103
|
+
behavior =
|
104
|
+
if options[:return]
|
105
|
+
lambda do |*args|
|
106
|
+
default_operation.call(*args)
|
107
|
+
return options[:return]
|
108
|
+
end
|
109
|
+
else
|
110
|
+
default_operation
|
111
|
+
end
|
112
|
+
|
113
|
+
meta_def method, &behavior
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
class Object
|
3
|
+
# Create a stub method on an object. Simply returns a value for a method call on
|
4
|
+
# an object.
|
5
|
+
#
|
6
|
+
# ==== Examples
|
7
|
+
#
|
8
|
+
# my_string = "a wooden rabbit"
|
9
|
+
# my_string.stub!(:retreat!, :return => "run away! run away!")
|
10
|
+
#
|
11
|
+
# # test/your_test.rb
|
12
|
+
# my_string.retreat! # => "run away! run away!"
|
13
|
+
#
|
14
|
+
def stub!(method_name, options = {}, &stubbed)
|
15
|
+
MotionSpec::Stubs.add(self, method_name)
|
16
|
+
|
17
|
+
behavior = (block_given? ? stubbed : -> { return options[:return] })
|
18
|
+
|
19
|
+
safe_meta_def method_name, &behavior
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module Kernel
|
24
|
+
# Create a pure stub object.
|
25
|
+
#
|
26
|
+
# ==== Examples
|
27
|
+
#
|
28
|
+
# stubbalicious = stub(:failure, "wat u say?")
|
29
|
+
# stubbalicious.failure # => "wat u say?"
|
30
|
+
#
|
31
|
+
def stub(method, options = {}, &block)
|
32
|
+
stub_object = Object.new
|
33
|
+
stub_object.stub!(method, options, &block)
|
34
|
+
|
35
|
+
stub_object
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module MotionSpec
|
3
|
+
class Stubs
|
4
|
+
class << self
|
5
|
+
def add(object, method)
|
6
|
+
stubs << [object, method]
|
7
|
+
end
|
8
|
+
|
9
|
+
def stubs
|
10
|
+
@stubs ||= []
|
11
|
+
end
|
12
|
+
|
13
|
+
def clear!
|
14
|
+
stubs.each { |object, method| object.reset(method) }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -26,7 +26,7 @@ module MotionSpec
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def handle_requirement_end(error)
|
29
|
-
|
29
|
+
unless error.empty?
|
30
30
|
puts "##teamcity[testFailed timestamp = '#{java_time}' message = '#{escape_message(error)}' name = '#{escape_message(@@description)}']\n\n"
|
31
31
|
end
|
32
32
|
duration = ((Time.now - @@started) * 1000).to_i
|
@@ -36,7 +36,7 @@ module MotionSpec
|
|
36
36
|
def handle_summary
|
37
37
|
print ErrorLog if Backtraces
|
38
38
|
puts '%d specifications (%d requirements), %d failures, %d errors' %
|
39
|
-
|
39
|
+
Counter.values_at(:specifications, :requirements, :failed, :errors)
|
40
40
|
end
|
41
41
|
|
42
42
|
def spaces
|
data/lib/motion-spec/should.rb
CHANGED
@@ -31,7 +31,7 @@ module MotionSpec
|
|
31
31
|
alias_method :a, :be
|
32
32
|
alias_method :an, :be
|
33
33
|
|
34
|
-
def satisfy(*args, &
|
34
|
+
def satisfy(*args, &_block)
|
35
35
|
if args.size == 1 && String === args.first
|
36
36
|
description = args.shift
|
37
37
|
else
|
@@ -82,7 +82,7 @@ module MotionSpec
|
|
82
82
|
end
|
83
83
|
|
84
84
|
def flunk(reason = 'Flunked')
|
85
|
-
|
85
|
+
fail Error.new(:failed, reason)
|
86
86
|
end
|
87
87
|
end
|
88
88
|
end
|
@@ -40,6 +40,8 @@ module MotionSpec
|
|
40
40
|
def run_after_filters
|
41
41
|
@ran_after_filters = true
|
42
42
|
execute_block { @after_filters.each { |f| @context.instance_eval(&f) } }
|
43
|
+
Mocks.clear!
|
44
|
+
Stubs.clear!
|
43
45
|
end
|
44
46
|
|
45
47
|
def run
|
@@ -66,7 +68,7 @@ module MotionSpec
|
|
66
68
|
def postpone_block(timeout = 1, &block)
|
67
69
|
# If an exception occurred, we definitely don't need to schedule any more blocks
|
68
70
|
return if @exception_occurred
|
69
|
-
|
71
|
+
fail MULTIPLE_POSTPONES_ERROR_MESSAGE if @postponed_block
|
70
72
|
|
71
73
|
@postponed_blocks_count += 1
|
72
74
|
@postponed_block = block
|
@@ -84,7 +86,7 @@ module MotionSpec
|
|
84
86
|
def postpone_block_until_change(object_to_observe, key_path, timeout = 1, &block)
|
85
87
|
# If an exception occurred, we definitely don't need to schedule any more blocks
|
86
88
|
return if @exception_occurred
|
87
|
-
|
89
|
+
fail MULTIPLE_POSTPONES_ERROR_MESSAGE if @postponed_block
|
88
90
|
|
89
91
|
@postponed_blocks_count += 1
|
90
92
|
@postponed_block = block
|
@@ -101,9 +103,11 @@ module MotionSpec
|
|
101
103
|
postponed_change_block_timeout_exceeded
|
102
104
|
end
|
103
105
|
|
104
|
-
|
106
|
+
# rubocop:disable Lint/UnusedMethodArgument
|
107
|
+
def observeValueForKeyPath(_key_path, ofObject:object, change:_, context:__)
|
105
108
|
resume
|
106
109
|
end
|
110
|
+
# rubocop:enable Lint/UnusedMethodArgument
|
107
111
|
|
108
112
|
def postponed_change_block_timeout_exceeded
|
109
113
|
remove_observer!
|
@@ -120,7 +124,7 @@ module MotionSpec
|
|
120
124
|
|
121
125
|
def postponed_block_timeout_exceeded
|
122
126
|
cancel_scheduled_requests!
|
123
|
-
execute_block {
|
127
|
+
execute_block { fail Error.new(:failed, "timeout exceeded: #{@context.name} - #{@description}") }
|
124
128
|
@postponed_blocks_count = 0
|
125
129
|
finish_spec
|
126
130
|
end
|
@@ -131,7 +135,8 @@ module MotionSpec
|
|
131
135
|
NSObject.cancelPreviousPerformRequestsWithTarget(self, selector: 'postponed_change_block_timeout_exceeded', object: nil)
|
132
136
|
end
|
133
137
|
remove_observer!
|
134
|
-
block
|
138
|
+
block = @postponed_block
|
139
|
+
@postponed_block = nil
|
135
140
|
run_postponed_block(block)
|
136
141
|
end
|
137
142
|
|
@@ -153,7 +158,7 @@ module MotionSpec
|
|
153
158
|
def finish_spec
|
154
159
|
if !@exception_occurred && Counter[:requirements] == @number_of_requirements_before
|
155
160
|
# the specification did not contain any requirements, so it flunked
|
156
|
-
execute_block {
|
161
|
+
execute_block { fail Error.new(:missing, "empty specification: #{@context.name} #{@description}") }
|
157
162
|
end
|
158
163
|
run_after_filters
|
159
164
|
exit_spec unless postponed?
|
@@ -181,9 +186,9 @@ module MotionSpec
|
|
181
186
|
if e.is_a?(Exception)
|
182
187
|
ErrorLog << "#{e.class}: #{e.message}\n"
|
183
188
|
lines = $DEBUG ? e.backtrace : e.backtrace.find_all { |line| line !~ /bin\/macbacon|\/mac_bacon\.rb:\d+/ }
|
184
|
-
lines.each_with_index
|
189
|
+
lines.each_with_index do |line, i|
|
185
190
|
ErrorLog << "\t#{line}#{i == 0 ? ": #{@context.name} - #{@description}" : ''}\n"
|
186
|
-
|
191
|
+
end
|
187
192
|
ErrorLog << "\n"
|
188
193
|
else
|
189
194
|
if defined?(NSException)
|
data/lib/motion-spec/version.rb
CHANGED
data/lib/motion-spec.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
2
|
unless defined?(Motion::Project::Config)
|
3
|
-
|
3
|
+
fail 'The MotionSpec gem must be required within a RubyMotion project Rakefile.'
|
4
4
|
end
|
5
5
|
|
6
6
|
require 'motion-require'
|
@@ -45,10 +45,13 @@ require_lib_files('matcher/*')
|
|
45
45
|
# Monkeypatch core objects to respond to test methods
|
46
46
|
require_lib_files('extensions/*')
|
47
47
|
|
48
|
+
# Allow method mocks and stubs
|
49
|
+
require_lib_files('mock/*')
|
50
|
+
|
48
51
|
# FIXME : Need better detection for iPhone Simulator
|
49
52
|
if defined?(UIDevice) &&
|
50
|
-
|
51
|
-
|
53
|
+
UIDevice.respond_to?('currentDevice') &&
|
54
|
+
!UIDevice.currentDevice.name =~ /(iPhone|iPad) Simulator/
|
52
55
|
|
53
56
|
module Kernel
|
54
57
|
def puts(*args)
|
@@ -74,8 +77,8 @@ module Motion
|
|
74
77
|
# NOTE: This line is commented out to avoid loading Bacon.
|
75
78
|
( # ['spec.rb'] +
|
76
79
|
Dir.glob(File.join('spec', 'helpers', '*.rb')) +
|
77
|
-
Dir.glob(File.join('project', 'template', App.template.to_s, 'spec-helpers', '*.rb')))
|
78
|
-
map { |x| File.expand_path(x) }
|
80
|
+
Dir.glob(File.join('project', 'template', App.template.to_s, 'spec-helpers', '*.rb')))
|
81
|
+
.map { |x| File.expand_path(x) }
|
79
82
|
end
|
80
83
|
end
|
81
84
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: motion-spec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonathan Bender
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-01-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: motion-require
|
@@ -92,6 +92,12 @@ files:
|
|
92
92
|
- lib/motion-spec/matcher/satisfy.rb
|
93
93
|
- lib/motion-spec/matcher/single_method.rb
|
94
94
|
- lib/motion-spec/matcher/start_with.rb
|
95
|
+
- lib/motion-spec/mock/method_bind_unbind.rb
|
96
|
+
- lib/motion-spec/mock/mock.rb
|
97
|
+
- lib/motion-spec/mock/mocks.rb
|
98
|
+
- lib/motion-spec/mock/proxy.rb
|
99
|
+
- lib/motion-spec/mock/stub.rb
|
100
|
+
- lib/motion-spec/mock/stubs.rb
|
95
101
|
- lib/motion-spec/output/colorized.rb
|
96
102
|
- lib/motion-spec/output/fast.rb
|
97
103
|
- lib/motion-spec/output/knock.rb
|
@@ -124,7 +130,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
124
130
|
version: '0'
|
125
131
|
requirements: []
|
126
132
|
rubyforge_project:
|
127
|
-
rubygems_version: 2.4.
|
133
|
+
rubygems_version: 2.4.6
|
128
134
|
signing_key:
|
129
135
|
specification_version: 4
|
130
136
|
summary: RubyMotion derivative of Bacon, which is a derivative of RSpec
|