motion-spec 0.4.2 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|