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.
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