serial-spec 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9848c116a4381a807460081064dc65beca99630a
4
- data.tar.gz: 31a670e4ab9f242257189a50368b0f87a7c133ca
3
+ metadata.gz: 042a2b062a70b8573348c2c0081eddf69e1394c0
4
+ data.tar.gz: 82c1fe7170df02c00c5208c92448c5bc41bab959
5
5
  SHA512:
6
- metadata.gz: 0a6f00d831f94fe3b7c5d7da43e36528e8e9dbfe1f7584536db7ad23af39d55bca90e4c035aab911e125301e06ff337fe521f3146dfc16afaaa7aa124064571c
7
- data.tar.gz: f9545df060cc2f81db1d93e7daa920ae18fe15a44b6e23da87cba2c2ed1a2f3d333f024946a4495f4ab21bcab9db740765a31611d42c770e5b75f0280c2770c1
6
+ metadata.gz: 7a3e5711246fa8f02e5b48e86e37712c0efc45e1459a38c4e9fae3c2cdb1f8bb5e2b9366182a9e0cbd0aad9bb66c2b8f9705ccb662a814c8e538814bc5359716
7
+ data.tar.gz: 1bad6f37298e3aed3943f586a18afa436cf32a6a64f2cc33557a992bbf862325f3b33e01ad38e42ebdeb65e7a7bf7e550fbab2c542d92fc2388417896a59f646
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  SerialSpec is designed provide some simple matchers and macros overlaying a simple `rack/test` setup for testing json apis more effectively.
4
4
 
5
- ## Usage
5
+ ## Basic Usage
6
6
 
7
7
  ```ruby
8
8
  require "spec_helper"
@@ -18,20 +18,6 @@ RSpec.describe "test" do
18
18
  end
19
19
  ```
20
20
 
21
- ## Testing architecture
22
-
23
- SerialSpec is assuming that you are:
24
-
25
- 1. setting up preconditions
26
- 2. executing 1 request
27
- 3. inspecting the response
28
-
29
- If you need more requests you should be using mocks and stubs.
30
-
31
- ### "But I need more than one request?"
32
-
33
- SerialSpec, unlike rails' out of the box request testing system is closer to a controller test, than rails' request test, which is more kin to an integration test. Some people recommend using actual requests for performing things like login, auth vs mocking. However, this library doesn't subscribe to that pattern. If you aren't ok with that, you should look elsewhere.
34
-
35
21
  ## Contributing
36
22
 
37
23
  1. Fork it ( https://github.com/blakechambers/serial-spec/fork )
data/lib/serial_spec.rb CHANGED
@@ -15,9 +15,16 @@ module SerialSpec
15
15
  include ItExpects
16
16
  include RequestResponse
17
17
  include RequestResponse::Helpers
18
+
18
19
  if defined?(ActiveModel::Serializer)
19
20
  require "serial_spec/request_response/provide_matcher"
20
21
  include RequestResponse::ProvideMatcher
22
+
23
+ if defined?(::RSpec) and
24
+ defined?(::RSpec::Core::Version::STRING) and
25
+ Gem::Version.new(::RSpec::Core::Version::STRING) >= Gem::Version.new("3.2.0")
26
+ require "serial_spec/request_response/include_provide_matcher"
27
+ end
21
28
  end
22
29
 
23
30
  SERIAL_VALID_VERBS = %w{GET POST PUT PATCH DELETE OPTIONS HEAD}
@@ -33,8 +40,15 @@ module SerialSpec
33
40
  if request_str.split(/\s+/).count == 2
34
41
  request_method_string, request_path_str = request_str.split(/\s+/)
35
42
  if SERIAL_VALID_VERBS.include?(request_method_string)
36
- request_method request_method_string
37
- request_path request_path_str
43
+ # Prefer preference to blocks, chances are the blocks need to be
44
+ # executed at a lower level
45
+ unless request_opts[:request_method] and request_opts[:request_method].instance_of?(InheritableAccessors::InheritableOptionAccessor::LetOption)
46
+ request_method request_method_string
47
+ end
48
+
49
+ unless request_opts[:request_path] and request_opts[:request_path].instance_of?(InheritableAccessors::InheritableOptionAccessor::LetOption)
50
+ request_path request_path_str
51
+ end
38
52
  end
39
53
  end
40
54
 
@@ -4,57 +4,126 @@ require "active_support/core_ext/hash/indifferent_access"
4
4
  module SerialSpec
5
5
 
6
6
  class ParsedBody
7
+ class MissingSelectorError < StandardError ; end
8
+ class NotAnEnumerableObject < StandardError ; end
7
9
 
8
- attr_reader :raw_body
9
- attr_reader :selector
10
+ include Enumerable
10
11
 
11
- def initialize(body)
12
- @selector = []
13
- @raw_body = JSON.parse(body)
12
+ attr_accessor :raw_body
13
+ attr_accessor :selector
14
+ attr_reader :body
15
+
16
+ def initialize(raw_body=nil, options={})
17
+ @selector = options[:selector] || []
18
+ @raw_body = raw_body
14
19
  end
15
20
 
16
- def self.add_selector(*methuds)
17
- methuds.each do |methud|
18
- class_eval <<-METHOD
19
- def #{methud.to_s}(*args)
20
- selector.push([#{methud.to_sym}, args])
21
- self
22
- end
23
- METHOD
24
- end
21
+ def body
22
+ @body ||= JSON.parse(raw_body)
25
23
  end
26
24
 
27
- add_selector :first, :last, :[]
25
+ def each(&block)
26
+ current_obj = clone.execute
27
+ raise NotAnEnumerableObject unless current_obj.kind_of?(Hash) or current_obj.kind_of?(Array)
28
+
29
+
30
+
31
+ if current_obj.kind_of?(Array)
32
+ current_obj.each_with_index do |item, i|
33
+ yield(
34
+ clone.tap do |b|
35
+ b.selector.push([:[], i])
36
+ end
37
+ )
38
+ end
39
+ else
40
+ current_obj.each do |key, value|
41
+ yield(
42
+ key,
43
+ clone.tap do |b|
44
+ b.selector.push([:[], key])
45
+ end
46
+ )
47
+ end
48
+ end
49
+ end
28
50
 
29
51
  def [](*args)
30
- selector.push([:[], *args])
31
- self
52
+ clone.tap do |b|
53
+ b.selector.push([:[], *args])
54
+ end
32
55
  end
33
56
 
34
57
  def first(*args)
35
- selector.push([:first])
36
- self
58
+ clone.tap do |b|
59
+ b.selector.push([:first])
60
+ end
37
61
  end
38
62
 
39
63
  def last(*args)
40
- selector.push([:last])
41
- self
64
+ clone.tap do |b|
65
+ b.selector.push([:last])
66
+ end
42
67
  end
43
68
 
44
69
  def execute
45
70
  copy = selector.clone
46
71
  selector.clear
47
- copy.inject(raw_body) do |remainder, method_and_args|
48
- begin
72
+ current_selector = []
73
+
74
+ failure = catch(:failed) do
75
+
76
+ return copy.inject(body) do |remainder, method_and_args|
77
+ current_selector << method_and_args
49
78
  methud, *args = method_and_args
79
+
80
+ if [:first, :last].include?(methud) || args.first.kind_of?(Fixnum)
81
+ throw(:failed, [:expected_array, remainder, current_selector, method_and_args]) unless remainder.kind_of?(Array)
82
+ else
83
+ throw(:failed, [:expected_object, remainder, current_selector, method_and_args]) unless remainder.kind_of?(Hash)
84
+ end
85
+
50
86
  if remainder.kind_of?(Hash)
51
87
  remainder.with_indifferent_access.send methud, *args
52
88
  else
53
89
  remainder.send methud, *args
54
90
  end
55
- rescue NoMethodError => ex
56
- raise ArgumentError, "could not find '#{methud.inspect}' \nfor:\n '#{remainder.inspect}' \nin\n #{raw_body}"
91
+
57
92
  end
93
+
94
+ end
95
+
96
+ if failure.kind_of?(Array)
97
+ raise MissingSelectorError, failed_message(*failure)
98
+ end
99
+ end
100
+
101
+ private ###############################
102
+
103
+ def initialize_copy(other)
104
+ @selector = other.selector.clone
105
+ @raw_body = other.raw_body
106
+ end
107
+
108
+ def formatted_selector(selector)
109
+ output = ""
110
+
111
+ selector.each do |item|
112
+ if item.first == :[]
113
+ output << "[#{item.last.inspect}]"
114
+ else
115
+ output << "[#{item.first.inspect}]"
116
+ end
117
+ end
118
+ output
119
+ end
120
+
121
+ def failed_message(msg, remainder, selector, current_selector)
122
+ case msg
123
+ when :expected_object
124
+ "expected an object to have #{formatted_selector([current_selector])}, but found #{remainder.class}:\"#{remainder}\" at #{formatted_selector(selector[0..-2])}"
125
+ when :expected_array
126
+ "expected an array at \"#{formatted_selector(selector[0..-2])}\", but found #{remainder.class}:'#{remainder}'"
58
127
  end
59
128
  end
60
129
 
@@ -1,5 +1,6 @@
1
1
  require "rack/test"
2
2
  require "inheritable_accessors/inheritable_hash_accessor"
3
+ require "inheritable_accessors/inheritable_option_accessor"
3
4
  require "serial_spec/request_response/helpers"
4
5
 
5
6
  module SerialSpec
@@ -7,15 +8,15 @@ module SerialSpec
7
8
  extend ActiveSupport::Concern
8
9
  include Rack::Test::Methods
9
10
  include InheritableAccessors::InheritableHashAccessor
11
+ include InheritableAccessors::InheritableOptionAccessor
10
12
  include Helpers
11
13
 
12
14
  included do
13
- include ::SerialSpec::RequestResponse::DSL
14
- extend ::SerialSpec::RequestResponse::DSL
15
-
16
15
  inheritable_hash_accessor :request_opts
17
16
  inheritable_hash_accessor :request_params
18
17
  inheritable_hash_accessor :request_envs
18
+
19
+ inheritable_option_accessor :request_path, :request_method, for: :request_opts
19
20
  end
20
21
 
21
22
  def perform_request!
@@ -25,30 +26,5 @@ module SerialSpec
25
26
  current_session.send :process_request, request_path, env
26
27
  end
27
28
 
28
- module DSL
29
-
30
- def request_path(new_path=nil)
31
- if new_path
32
- request_opts[:path] = new_path
33
- else
34
- path = request_opts[:path]
35
- return path if path
36
- raise "You must configure a path"
37
- end
38
- end
39
-
40
- # GET, POST, PUT, DELETE, OPTIONS, HEAD
41
- def request_method(new_method=nil)
42
- if new_method
43
- request_opts[:method] = new_method
44
- else
45
- methud = request_opts[:method]
46
- return methud if methud
47
- raise "You must configure a request method"
48
- end
49
- end
50
-
51
- end
52
-
53
29
  end
54
- end
30
+ end
@@ -0,0 +1,7 @@
1
+ RSpec::Matchers.define :include_a_provided do |expected|
2
+ match do |actual_list|
3
+ actual_list.detect do |actual|
4
+ SerialSpec::RequestResponse::ProvideMatcher::Provide.new(expected).matches?(actual)
5
+ end
6
+ end
7
+ end
@@ -13,7 +13,6 @@ module SerialSpec
13
13
  class SerializerNotFound < StandardError ; end
14
14
 
15
15
  attr_reader :as_serializer
16
- attr_reader :with_root
17
16
  attr_reader :expected
18
17
  attr_reader :actual
19
18
 
@@ -22,11 +21,14 @@ module SerialSpec
22
21
  def initialize(expected,options={})
23
22
  @expected = expected
24
23
  @as_serializer = options[:as]
25
- @with_root = options[:with_root]
24
+
25
+ if @as_serializer and not @as_serializer.instance_methods.include?(:serializable_hash)
26
+ raise ArgumentError, 'must be an active model serializer'
27
+ end
26
28
  end
27
29
 
28
30
  def actual_to_hash(actual)
29
- if actual.kind_of? SerialSpec::ParsedBody
31
+ if actual.kind_of? SerialSpec::ParsedBody
30
32
  strip_hypermedia(actual.execute)
31
33
  else
32
34
  strip_hypermedia(actual)
@@ -35,23 +37,23 @@ module SerialSpec
35
37
 
36
38
  def resource_serializer
37
39
  if as_serializer
38
- as_serializer.new(expected, root: with_root)
40
+ as_serializer.new(expected, root: nil)
39
41
  else
40
42
  unless expected.respond_to?(:active_model_serializer)
41
43
  throw(:failed, :serializer_not_specified_on_class)
42
44
  end
43
- expected.active_model_serializer.new(expected,root: with_root)
45
+ expected.active_model_serializer.new(expected, root: nil)
44
46
  end
45
47
  end
46
48
 
47
49
  def collection_serializer
48
50
  if as_serializer
49
- ActiveModel::ArraySerializer.new(expected,serializer: as_serializer, root: with_root )
51
+ ActiveModel::ArraySerializer.new(expected, serializer: as_serializer, root: nil )
50
52
  else
51
- ActiveModel::ArraySerializer.new(expected, root: with_root)
53
+ ActiveModel::ArraySerializer.new(expected, root: nil)
52
54
  end
53
55
  end
54
-
56
+
55
57
  # to_json first to normalize hash and all it's members
56
58
  # the parse into JSON to compare to ParsedBody hash
57
59
  def expected_to_hash
@@ -65,7 +67,7 @@ module SerialSpec
65
67
 
66
68
  def normalize_data(data)
67
69
  if data.kind_of?(Array)
68
- data.each_with_index do |el,index|
70
+ data.each_with_index do |el, index|
69
71
  data[index] = normalize_data(el)
70
72
  end
71
73
  elsif data.kind_of?(Hash)
@@ -74,17 +76,18 @@ module SerialSpec
74
76
  end
75
77
 
76
78
  def strip_hypermedia(actual)
77
- actual.delete_if {|k,v| HYPERMEDIA_ATTRIBUTES.include?(k) }
79
+ (actual || {}).delete_if {|k,v| HYPERMEDIA_ATTRIBUTES.include?(k) }
78
80
  end
79
81
 
80
82
  def matches?(actual)
81
-
82
83
  failure = catch(:failed) do
84
+
83
85
  unless actual.kind_of?(Hash) || actual.kind_of?(Array) || actual.kind_of?(ParsedBody)
84
86
  throw(:failed, :response_not_valid)
85
87
  end
86
- @actual = actual_to_hash(actual)
87
- @expected = expected_to_hash
88
+
89
+ @actual = actual_to_hash(actual)
90
+ @expected = expected_to_hash
88
91
 
89
92
  if @actual == @expected
90
93
  #noop - specs pass
@@ -92,17 +95,17 @@ module SerialSpec
92
95
  throw(:failed, :response_and_model_dont_match)
93
96
  end
94
97
  end
95
- @failure_message = failed_message(failure) if failure
98
+ @failure_message = failed_message(failure) if failure
96
99
  !failure
97
100
  end
98
101
 
99
102
  # when rspec asserts eq
100
103
  alias == matches?
101
104
 
102
- def failed_message(msg)
105
+ def failed_message(msg)
103
106
  case msg
104
107
  when :response_and_model_dont_match
105
- "Actual and Expected do not match.\nActual #{actual}\nExpected #{expected}"
108
+ "Actual and Expected do not match.\nActual #{actual}\nExpected #{expected}"
106
109
  when :serializer_not_specified_on_class
107
110
  "'active_model_serializer' not implemented on expected, see ehttp://bit.ly/18TdmXs"
108
111
  when :response_not_valid
@@ -1,3 +1,3 @@
1
1
  module SerialSpec
2
- VERSION = "0.2.1"
2
+ VERSION = "0.3.0"
3
3
  end
data/serial-spec.gemspec CHANGED
@@ -18,6 +18,6 @@ Gem::Specification.new do |spec|
18
18
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_runtime_dependency "inheritable_accessors", ">= 0.1.1"
21
+ spec.add_runtime_dependency "inheritable_accessors", ">= 0.1.2"
22
22
  spec.add_runtime_dependency "activesupport", ">= 3.2.0"
23
23
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: serial-spec
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Blake Chambers
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-03-26 00:00:00.000000000 Z
11
+ date: 2015-04-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: inheritable_accessors
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.1.1
19
+ version: 0.1.2
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 0.1.1
26
+ version: 0.1.2
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activesupport
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -63,6 +63,7 @@ files:
63
63
  - lib/serial_spec/parsed_body.rb
64
64
  - lib/serial_spec/request_response.rb
65
65
  - lib/serial_spec/request_response/helpers.rb
66
+ - lib/serial_spec/request_response/include_provide_matcher.rb
66
67
  - lib/serial_spec/request_response/provide_matcher.rb
67
68
  - lib/serial_spec/version.rb
68
69
  - serial-spec.gemspec