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.
Files changed (39) hide show
  1. checksums.yaml +8 -8
  2. data/LICENSE +3 -1
  3. data/README.md +13 -0
  4. data/Rakefile +1 -1
  5. data/app/app_delegate.rb +3 -1
  6. data/lib/motion-spec/context.rb +17 -1
  7. data/lib/motion-spec/context_helper/memoized_helpers.rb +1 -1
  8. data/lib/motion-spec/core.rb +4 -4
  9. data/lib/motion-spec/expectation.rb +1 -1
  10. data/lib/motion-spec/extensions/boolean.rb +13 -4
  11. data/lib/motion-spec/extensions/numeric.rb +3 -1
  12. data/lib/motion-spec/extensions/proc.rb +2 -2
  13. data/lib/motion-spec/matcher/be_false.rb +1 -1
  14. data/lib/motion-spec/matcher/be_nil.rb +2 -2
  15. data/lib/motion-spec/matcher/be_true.rb +1 -1
  16. data/lib/motion-spec/matcher/be_within.rb +2 -2
  17. data/lib/motion-spec/matcher/change.rb +3 -3
  18. data/lib/motion-spec/matcher/end_with.rb +1 -1
  19. data/lib/motion-spec/matcher/have_generic.rb +1 -1
  20. data/lib/motion-spec/matcher/have_items.rb +1 -1
  21. data/lib/motion-spec/matcher/include.rb +1 -1
  22. data/lib/motion-spec/matcher/match_array.rb +1 -1
  23. data/lib/motion-spec/matcher/raise_error.rb +9 -9
  24. data/lib/motion-spec/matcher/respond_to.rb +1 -1
  25. data/lib/motion-spec/matcher/satisfy.rb +2 -2
  26. data/lib/motion-spec/matcher/single_method.rb +1 -1
  27. data/lib/motion-spec/matcher/start_with.rb +1 -1
  28. data/lib/motion-spec/mock/method_bind_unbind.rb +57 -0
  29. data/lib/motion-spec/mock/mock.rb +63 -0
  30. data/lib/motion-spec/mock/mocks.rb +29 -0
  31. data/lib/motion-spec/mock/proxy.rb +115 -0
  32. data/lib/motion-spec/mock/stub.rb +37 -0
  33. data/lib/motion-spec/mock/stubs.rb +18 -0
  34. data/lib/motion-spec/output/ruby_mine.rb +2 -2
  35. data/lib/motion-spec/should.rb +2 -2
  36. data/lib/motion-spec/specification.rb +13 -8
  37. data/lib/motion-spec/version.rb +1 -1
  38. data/lib/motion-spec.rb +8 -5
  39. metadata +9 -3
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- YjUwZGM1OTczY2NkNzQyYzliMzdjNzU4Yzk4YmY0NDAyYTE2Mzg1NA==
4
+ NGM3ZTc2NzgwMWRhMjAyNDg1OTgxNTYyNDk5YjAwNjk5MDEzNTA1ZQ==
5
5
  data.tar.gz: !binary |-
6
- MTZjODFkOWNkNTMwMDU2MjA1NTM2ZWU2ODM5ODg5YmZjNjYyODNhOA==
6
+ NDQwODRjNDY5ZGNjZmExYzExMThmM2Y2MjdlZTMwYTgyZTdhMDkzYg==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- NGUwNDFjMTY5NjQzZDc5NzAzMzUzZTMwNGE1MjllNTI3MWZhODEyZjg5OTli
10
- NjY5ZmE0N2Y0NWQ1NTkyNGJkNzQxMDc5ZTgyNjE0ZTgyMWVhNmJiYWZjNjRm
11
- NTY1ZDhkMTJiZjk2ZmU4NTEzNmExZjE3NzRkOWE4ZDdkMjIxYzg=
9
+ MGY3NDZkZTgxNjkxYzBkMzUyMjA1MmI1ODcxYzlmNTI1YTA4MDhhYjYzMjIx
10
+ MTUzZjMxZGU5MDZmNmUzM2EyMTYzNzBhMDQxYTE5ODliNDQ0OWEyZjZjNjFk
11
+ YmE1MmE4YjA5OTU3MWUwNzIzYmY2ZGIyMjk5NDNjNWMwZWJiMjc=
12
12
  data.tar.gz: !binary |-
13
- N2Y3ZDE3ZjU4ODIwMTkyZDU4NWY1ODVkNjI4Y2JkMDc1NDhkMWRhZmVmMzI4
14
- Y2MyZjFiMzViOTE2ZjYzYTU4ZTE4NGFlZTAxMGY3MDMzYzNmZDRkZWNjNDJj
15
- NzlmNTU3YWE0ODNmZjM1ZWJiNjRhNjEyMDM0OTgwYjM4NTU2N2U=
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
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env rake
2
- $:.unshift('/Library/RubyMotion/lib')
2
+ $LOAD_PATH.unshift('/Library/RubyMotion/lib')
3
3
  require 'motion/project/template/ios'
4
4
  require 'bundler/gem_tasks'
5
5
  Bundler.setup
data/app/app_delegate.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  class AppDelegate
3
- def application(application, didFinishLaunchingWithOptions:launchOptions)
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
@@ -38,7 +38,7 @@ module MotionSpec
38
38
  @specifications[@current_specification_index]
39
39
  end
40
40
 
41
- def specification_did_finish(spec)
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
- raise '#let or #subject called without a block' unless block_given?
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
@@ -7,9 +7,9 @@ module MotionSpec
7
7
 
8
8
  Counter = Hash.new(0)
9
9
  ErrorLog = ''
10
- Shared = Hash.new { |_, name|
11
- raise NameError, "no such context: #{name.inspect}"
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(context)
77
+ def self.context_did_finish(_context)
78
78
  return if Platform.android?
79
79
 
80
80
  handle_specification_end
@@ -22,7 +22,7 @@ module MotionSpec
22
22
  end
23
23
 
24
24
  def fail(matcher, negated)
25
- raise matcher.fail!(@subject, negated, &@subject_block)
25
+ fail matcher.fail!(@subject, negated, &@subject_block)
26
26
  end
27
27
 
28
28
  def assert
@@ -1,13 +1,22 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  class Object
3
- def true?; false; end
4
- def false?; false; end
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?; true; end
13
+ def true?
14
+ true
15
+ end
9
16
  end
10
17
 
11
18
  class FalseClass
12
- def false?; true; end
19
+ def false?
20
+ true
21
+ end
13
22
  end
@@ -1,6 +1,8 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  class Numeric
3
3
  def close?(to, delta)
4
- (to.to_f - self).abs <= delta.to_f rescue false
4
+ (to.to_f - self).abs <= delta.to_f
5
+ rescue
6
+ false
5
7
  end
6
8
  end
@@ -9,10 +9,10 @@ class Proc
9
9
  end
10
10
 
11
11
  def throw?(sym)
12
- catch(sym) {
12
+ catch(sym) do
13
13
  call
14
14
  return false
15
- }
15
+ end
16
16
  true
17
17
  end
18
18
 
@@ -8,7 +8,7 @@ module MotionSpec
8
8
 
9
9
  def fail!(subject, negated)
10
10
  message = FailMessageRenderer.message_for_be_false(negated, subject)
11
- raise FailedExpectation.new(message)
11
+ fail FailedExpectation.new(message)
12
12
  end
13
13
  end
14
14
  end
@@ -3,11 +3,11 @@ module MotionSpec
3
3
  module Matcher
4
4
  class BeNil
5
5
  def matches?(value)
6
- value == nil
6
+ value.nil?
7
7
  end
8
8
 
9
9
  def fail!(subject, negated)
10
- raise FailedExpectation.new(
10
+ fail FailedExpectation.new(
11
11
  FailMessageRenderer.message_for_be_nil(negated, subject)
12
12
  )
13
13
  end
@@ -7,7 +7,7 @@ module MotionSpec
7
7
  end
8
8
 
9
9
  def fail!(subject, negated)
10
- raise FailedExpectation.new(
10
+ fail FailedExpectation.new(
11
11
  FailMessageRenderer.message_for_be_true(negated, subject)
12
12
  )
13
13
  end
@@ -14,12 +14,12 @@ module MotionSpec
14
14
  end
15
15
 
16
16
  def matches?(subject)
17
- raise InvalidMatcher.new(INVALID_MATCH_ERROR) unless @center_value
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
- raise FailedExpectation.new(
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?(subject, &expectation_block)
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!(subject, negated)
27
- raise FailedExpectation.new(
26
+ def fail!(_subject, negated)
27
+ fail FailedExpectation.new(
28
28
  FailMessageRenderer.message_for_change(
29
29
  negated, @change_amount, @value_diff
30
30
  )
@@ -11,7 +11,7 @@ module MotionSpec
11
11
  end
12
12
 
13
13
  def fail!(subject, negated)
14
- raise FailedExpectation.new(
14
+ fail FailedExpectation.new(
15
15
  FailMessageRenderer.message_for_end_with(
16
16
  negated, subject, @end_string
17
17
  )
@@ -12,7 +12,7 @@ module MotionSpec
12
12
  end
13
13
 
14
14
  def fail!(subject, negated)
15
- raise FailedExpectation.new(
15
+ fail FailedExpectation.new(
16
16
  FailMessageRenderer.message_for_have_generic(
17
17
  negated, subject, @method_name, @args
18
18
  )
@@ -18,7 +18,7 @@ module MotionSpec
18
18
  end
19
19
 
20
20
  def fail!(subject, negated)
21
- raise FailedExpectation.new(
21
+ fail FailedExpectation.new(
22
22
  FailMessageRenderer.message_for_have_items(
23
23
  negated, subject, @number_of_items, subject.size, @key_type_name
24
24
  )
@@ -11,7 +11,7 @@ module MotionSpec
11
11
  end
12
12
 
13
13
  def fail!(subject, negated)
14
- raise FailedExpectation.new(
14
+ fail FailedExpectation.new(
15
15
  FailMessageRenderer.message_for_include(negated, subject, @values)
16
16
  )
17
17
  end
@@ -17,7 +17,7 @@ module MotionSpec
17
17
  end
18
18
 
19
19
  def fail!(subject_array, negated)
20
- raise FailedExpectation.new(
20
+ fail FailedExpectation.new(
21
21
  FailMessageRenderer.message_for_match_array(
22
22
  negated, subject_array, @expected_array
23
23
  )
@@ -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?(value, &block)
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
- when String
23
- exception.message.include?(@error_message)
24
- when Regexp
25
- @error_message.match(exception.message)
26
- else
27
- false
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!(subject, negated)
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
- raise FailedExpectation.new(
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
@@ -29,7 +29,7 @@ module MotionSpec
29
29
  end
30
30
 
31
31
  def fail!(subject, negated)
32
- raise FailedExpectation.new(
32
+ fail FailedExpectation.new(
33
33
  FailMessageRenderer.message_for_respond_to(
34
34
  negated, subject, @method_name, @number_of_args
35
35
  )
@@ -10,8 +10,8 @@ module MotionSpec
10
10
  @condition_block.call(*values)
11
11
  end
12
12
 
13
- def fail!(subject, negated)
14
- raise FailedExpectation.new(
13
+ def fail!(_subject, negated)
14
+ fail FailedExpectation.new(
15
15
  FailMessageRenderer.message_for_satisfy(negated)
16
16
  )
17
17
  end
@@ -12,7 +12,7 @@ module MotionSpec
12
12
  end
13
13
 
14
14
  def fail!(subject, negated)
15
- raise FailedExpectation.new(fail_message(subject, negated))
15
+ fail FailedExpectation.new(fail_message(subject, negated))
16
16
  end
17
17
 
18
18
  def fail_message(subject, negated = false)
@@ -11,7 +11,7 @@ module MotionSpec
11
11
  end
12
12
 
13
13
  def fail!(subject, negated)
14
- raise FailedExpectation.new(
14
+ fail FailedExpectation.new(
15
15
  FailMessageRenderer.message_for_start_with(
16
16
  negated, subject, @start_string
17
17
  )
@@ -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
- if !error.empty?
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
- Counter.values_at(:specifications, :requirements, :failed, :errors)
39
+ Counter.values_at(:specifications, :requirements, :failed, :errors)
40
40
  end
41
41
 
42
42
  def spaces
@@ -31,7 +31,7 @@ module MotionSpec
31
31
  alias_method :a, :be
32
32
  alias_method :an, :be
33
33
 
34
- def satisfy(*args, &block)
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
- raise Error.new(:failed, reason)
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
- raise MULTIPLE_POSTPONES_ERROR_MESSAGE if @postponed_block
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
- raise MULTIPLE_POSTPONES_ERROR_MESSAGE if @postponed_block
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
- def observeValueForKeyPath(key_path, ofObject:object, change:_, context:__)
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 { raise Error.new(:failed, "timeout exceeded: #{@context.name} - #{@description}") }
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, @postponed_block = @postponed_block, nil
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 { raise Error.new(:missing, "empty specification: #{@context.name} #{@description}") }
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 { |line, i|
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)
@@ -1,5 +1,5 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
 
3
3
  module MotionSpec
4
- VERSION = '0.4.2'
4
+ VERSION = '0.5.0'
5
5
  end
data/lib/motion-spec.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  unless defined?(Motion::Project::Config)
3
- raise 'The MotionSpec gem must be required within a RubyMotion project Rakefile.'
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
- UIDevice.respond_to?('currentDevice') &&
51
- !UIDevice.currentDevice.name =~ /(iPhone|iPad) Simulator/
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.2
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: 2015-11-06 00:00:00.000000000 Z
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.8
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