api-tester 1.1.1 → 1.1.2

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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/dependabot.yml +29 -0
  3. data/.github/workflows/push.yml +1 -1
  4. data/.github/workflows/test.yml +3 -3
  5. data/.rubocop.yml +136 -26
  6. data/README.md +1 -1
  7. data/api-tester.gemspec +3 -3
  8. data/lib/api-tester/config.rb +1 -2
  9. data/lib/api-tester/definition/boundary_case.rb +1 -3
  10. data/lib/api-tester/definition/contract.rb +4 -4
  11. data/lib/api-tester/definition/endpoint.rb +35 -17
  12. data/lib/api-tester/definition/fields/array_field.rb +8 -4
  13. data/lib/api-tester/definition/fields/boolean_field.rb +9 -2
  14. data/lib/api-tester/definition/fields/email_field.rb +16 -2
  15. data/lib/api-tester/definition/fields/enum_field.rb +8 -4
  16. data/lib/api-tester/definition/fields/field.rb +12 -6
  17. data/lib/api-tester/definition/fields/number_field.rb +13 -2
  18. data/lib/api-tester/definition/fields/object_field.rb +8 -4
  19. data/lib/api-tester/definition/fields/plain_array_field.rb +2 -2
  20. data/lib/api-tester/definition/method.rb +1 -3
  21. data/lib/api-tester/definition/request.rb +7 -10
  22. data/lib/api-tester/definition/response.rb +11 -4
  23. data/lib/api-tester/method_case_test.rb +8 -8
  24. data/lib/api-tester/modules/benchmark_module.rb +35 -0
  25. data/lib/api-tester/modules/extra_verbs.rb +7 -1
  26. data/lib/api-tester/modules/good_case.rb +1 -1
  27. data/lib/api-tester/modules/good_variations.rb +69 -0
  28. data/lib/api-tester/modules/injection_module.rb +12 -6
  29. data/lib/api-tester/modules/missing_resource.rb +64 -0
  30. data/lib/api-tester/modules/server_information.rb +1 -2
  31. data/lib/api-tester/modules/typo.rb +3 -1
  32. data/lib/api-tester/modules/unused_fields.rb +1 -1
  33. data/lib/api-tester/reporter/api_report.rb +3 -2
  34. data/lib/api-tester/reporter/missing_field_report.rb +1 -4
  35. data/lib/api-tester/reporter/report.rb +2 -6
  36. data/lib/api-tester/reporter/response_time_report.rb +24 -0
  37. data/lib/api-tester/reporter/status_code_report.rb +1 -2
  38. data/lib/api-tester/test_helper.rb +2 -0
  39. data/lib/api-tester/util/response_evaluator.rb +37 -22
  40. data/lib/api-tester/util/supported_verbs.rb +2 -2
  41. data/lib/api-tester/version.rb +1 -1
  42. metadata +14 -9
@@ -5,10 +5,7 @@ require 'api-tester/definition/boundary_case'
5
5
  module ApiTester
6
6
  # Class for defining requests in a contract
7
7
  class Request
8
- attr_accessor :definition
9
- attr_accessor :header_fields
10
- attr_accessor :fields
11
- attr_accessor :query_params
8
+ attr_accessor :definition, :header_fields, :fields, :query_params
12
9
 
13
10
  def initialize
14
11
  self.fields = []
@@ -27,7 +24,7 @@ module ApiTester
27
24
  end
28
25
 
29
26
  def default_query
30
- query_params.map { |param| "#{param.name}=#{param.default_value}" }.join('&')
27
+ query_params.map { |param| "#{param.name}=#{param.default}" }.join('&')
31
28
  end
32
29
 
33
30
  def add_header_field(new_header)
@@ -39,7 +36,7 @@ module ApiTester
39
36
  response = {}
40
37
  fields.each do |field|
41
38
  if field.required == true
42
- response[field.name] = field.default_value
39
+ response[field.name] = field.default
43
40
  end
44
41
  end
45
42
  response
@@ -50,17 +47,17 @@ module ApiTester
50
47
  end
51
48
 
52
49
  def default_headers
53
- if header_fields != []
54
- headers
55
- else
50
+ if header_fields == []
56
51
  { content_type: :json, accept: :json }
52
+ else
53
+ headers
57
54
  end
58
55
  end
59
56
 
60
57
  def headers
61
58
  header_response = {}
62
59
  header_fields.each do |header_field|
63
- header_response[header_field.name] = header_field.default_value
60
+ header_response[header_field.name] = header_field.default
64
61
  end
65
62
  header_response
66
63
  end
@@ -3,8 +3,7 @@
3
3
  module ApiTester
4
4
  # Class for defining expected responses
5
5
  class Response
6
- attr_accessor :code
7
- attr_accessor :body
6
+ attr_accessor :code, :body
8
7
 
9
8
  def initialize(status_code: 200)
10
9
  self.code = status_code
@@ -19,7 +18,11 @@ module ApiTester
19
18
  def to_s
20
19
  des = {}
21
20
  body.map do |f|
22
- des[f.name] = field_display f
21
+ if f.has_key
22
+ des[f.name] = field_display f
23
+ else
24
+ des = field_display f
25
+ end
23
26
  end
24
27
  des.to_json
25
28
  end
@@ -29,7 +32,11 @@ module ApiTester
29
32
  if field.subfields?
30
33
  des = {}
31
34
  field.fields.map do |f|
32
- des[f.name] = field_display f
35
+ if f.has_key
36
+ des[f.name] = field_display f
37
+ else
38
+ des = field_display f
39
+ end
33
40
  end
34
41
  des.to_json
35
42
  end
@@ -1,16 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'api-tester/util/response_evaluator.rb'
3
+ require 'api-tester/util/response_evaluator'
4
4
 
5
5
  module ApiTester
6
6
  # Class for testing methods
7
7
  class MethodCaseTest
8
- attr_accessor :expected_response
9
- attr_accessor :payload
10
- attr_accessor :response
11
- attr_accessor :reports
12
- attr_accessor :url
13
- attr_accessor :module_name
8
+ attr_accessor :expected_response, :payload, :response, :reports, :url, :module_name
14
9
 
15
10
  def initialize(response:, payload:, expected_response:, url:, verb:, module_name:)
16
11
  self.payload = payload
@@ -22,6 +17,7 @@ module ApiTester
22
17
  end
23
18
 
24
19
  def response_code_report
20
+ print 'F'
25
21
  report = StatusCodeReport.new description: "#{module_name} - Incorrect response code",
26
22
  url: url,
27
23
  request: payload,
@@ -32,6 +28,7 @@ module ApiTester
32
28
  end
33
29
 
34
30
  def missing_field_report(field)
31
+ print 'F'
35
32
  report = Report.new description: "#{module_name} - Missing field #{field}",
36
33
  url: url,
37
34
  request: payload,
@@ -42,6 +39,7 @@ module ApiTester
42
39
  end
43
40
 
44
41
  def extra_field_report(field)
42
+ print 'F'
45
43
  report = Report.new description: "#{module_name} - Found extra field #{field}",
46
44
  url: url,
47
45
  request: payload,
@@ -53,6 +51,7 @@ module ApiTester
53
51
 
54
52
  def check
55
53
  if check_response_code
54
+ print '.'
56
55
  evaluator = ApiTester::ResponseEvaluator.new actual_body: json_parse(response.body),
57
56
  expected_fields: expected_response
58
57
  evaluator.missing_fields.map { |field| missing_field_report(field) }
@@ -63,7 +62,8 @@ module ApiTester
63
62
  end
64
63
 
65
64
  def check_response_code
66
- if response.code != expected_response.code
65
+ if response && (response.code != expected_response.code)
66
+ print 'F'
67
67
  response_code_report
68
68
  return false
69
69
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'api-tester/reporter/response_time_report'
4
+
5
+ module ApiTester
6
+ # Checks the response times collected during the test run
7
+ # Note: Needs at least one calling module, like GoodCase, to work
8
+ module BenchmarkModule
9
+ def self.go(contract)
10
+ reports = []
11
+
12
+ contract.endpoints.each do |endpoint|
13
+ longest_time = endpoint.longest_time
14
+ longest_time[:time] = longest_time[:time] * 1000.0 # Convert from seconds to ms
15
+ if longest_time[:time] > contract.max_time
16
+ print 'F'
17
+ reports << ResponseTimeReport.new(url: endpoint.url,
18
+ verb: longest_time[:verb],
19
+ payload: longest_time[:payload],
20
+ max_time: contract.max_time,
21
+ actual_time: longest_time[:time],
22
+ description: 'BenchmarkModule')
23
+ else
24
+ print '.'
25
+ end
26
+ end
27
+
28
+ reports
29
+ end
30
+
31
+ def self.order
32
+ 99
33
+ end
34
+ end
35
+ end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'api-tester/util/supported_verbs'
4
+ require 'api-tester/definition/method'
5
+ require 'api-tester/method_case_test'
4
6
 
5
7
  module ApiTester
6
8
  # Check verbs not explicitly defined in contract
@@ -9,7 +11,7 @@ module ApiTester
9
11
  reports = []
10
12
 
11
13
  contract.endpoints.each do |endpoint|
12
- extras = ApiTester::SupportedVerbs.all - endpoint.verbs
14
+ extras = supported_verbs - endpoint.verbs
13
15
  headers = endpoint.methods[0].request.default_headers
14
16
  extras.each do |verb|
15
17
  verb_case = BoundaryCase.new description: "Verb check with #{verb} for #{endpoint.name}",
@@ -34,6 +36,10 @@ module ApiTester
34
36
  reports
35
37
  end
36
38
 
39
+ def self.supported_verbs
40
+ ApiTester::SupportedVerbs.all
41
+ end
42
+
37
43
  def self.order
38
44
  3
39
45
  end
@@ -11,7 +11,7 @@ module ApiTester
11
11
 
12
12
  contract.endpoints.each do |endpoint|
13
13
  endpoint.methods.each do |method|
14
- default_case = BoundaryCase.new description: contract.base_url + endpoint.url,
14
+ default_case = BoundaryCase.new description: contract.base_url + endpoint.display_url,
15
15
  payload: method.request.default_payload,
16
16
  headers: method.request.default_headers
17
17
  response = endpoint.call base_url: contract.base_url,
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'api-tester/reporter/status_code_report'
4
+ require 'api-tester/method_case_test'
5
+
6
+ module ApiTester
7
+ # Checks the good case as defined in contract
8
+ module GoodVariations
9
+ def self.go(contract)
10
+ reports = []
11
+
12
+ contract.endpoints.each do |endpoint|
13
+ endpoint.methods.each do |method|
14
+ method.request.fields.each do |field|
15
+ field.good_cases.each do |value|
16
+ payload = method.request.default_payload
17
+ payload[field.name] = value
18
+ call = BoundaryCase.new description: contract.base_url + endpoint.display_url,
19
+ payload: payload,
20
+ headers: method.request.default_headers
21
+ response = endpoint.call base_url: contract.base_url,
22
+ method: method,
23
+ payload: payload,
24
+ headers: call.headers
25
+ test = GoodVariationTest.new response: response,
26
+ url: contract.base_url + endpoint.url,
27
+ method: method
28
+ reports.concat test.check
29
+ end
30
+ end
31
+ method.request.query_params.each do |field|
32
+ field.good_cases.each do |value|
33
+ payload = method.request.default_payload
34
+ payload[field.name] = value
35
+ call = BoundaryCase.new description: contract.base_url + endpoint.display_url,
36
+ payload: payload,
37
+ headers: method.request.default_headers
38
+ response = endpoint.call base_url: contract.base_url,
39
+ method: method,
40
+ payload: payload,
41
+ headers: call.headers
42
+ test = GoodVariationTest.new response: response,
43
+ url: contract.base_url + endpoint.url,
44
+ method: method
45
+ reports.concat test.check
46
+ end
47
+ end
48
+ end
49
+ end
50
+ reports
51
+ end
52
+
53
+ def self.order
54
+ 1
55
+ end
56
+ end
57
+
58
+ # Test layout used by module
59
+ class GoodVariationTest < MethodCaseTest
60
+ def initialize(response:, url:, method:)
61
+ super response: response,
62
+ payload: method.request.default_payload,
63
+ expected_response: method.expected_response,
64
+ url: url,
65
+ verb: method.verb,
66
+ module_name: 'GoodVariationsModule'
67
+ end
68
+ end
69
+ end
@@ -21,7 +21,7 @@ module ApiTester
21
21
 
22
22
  method.request.fields.each do |field|
23
23
  sql_injections.each do |injection|
24
- injection_value = "#{field.default_value}#{injection}"
24
+ injection_value = "#{field.default}#{injection}"
25
25
  payload = method.request.altered_payload field_name: field.name,
26
26
  value: injection_value
27
27
  response = endpoint.call base_url: base_url,
@@ -41,7 +41,12 @@ module ApiTester
41
41
  end
42
42
 
43
43
  def self.check_response(response, endpoint)
44
- response.code == 200 || check_error(response, endpoint)
44
+ if response.code == 200 || check_error(response, endpoint)
45
+ print '.'
46
+ return true
47
+ end
48
+ print 'F'
49
+ false
45
50
  end
46
51
 
47
52
  def self.check_error(response, endpoint)
@@ -54,15 +59,16 @@ module ApiTester
54
59
  response.code == endpoint.bad_request_response.code &&
55
60
  missing_fields.size.zero? && extra_fields.size.zero?
56
61
  end
62
+
63
+ def self.order
64
+ 5
65
+ end
57
66
  end
58
67
  end
59
68
 
60
69
  # Report for InjectionModule
61
70
  class InjectionReport
62
- attr_accessor :injection_type
63
- attr_accessor :url
64
- attr_accessor :payload
65
- attr_accessor :response
71
+ attr_accessor :injection_type, :url, :payload, :response
66
72
 
67
73
  def initialize(injection_type, url, payload, response)
68
74
  self.injection_type = injection_type
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'api-tester/reporter/status_code_report'
4
+ require 'api-tester/util/supported_verbs'
5
+ require 'pry'
6
+
7
+ module ApiTester
8
+ # Module checking various not found scenarios
9
+ module MissingResource
10
+ def self.go(contract)
11
+ reports = []
12
+
13
+ contract.endpoints.each do |endpoint|
14
+ endpoint.path_params.each do |path_param|
15
+ bad_resource = endpoint.relative_url.gsub("{#{path_param}}", 'gibberish')
16
+
17
+ bad_endpoint = ApiTester::Endpoint.new name: 'Bad Resource',
18
+ relative_url: bad_resource
19
+ method = ApiTester::Method.new verb: ApiTester::SupportedVerbs::GET,
20
+ response: ApiTester::Response.new(
21
+ status_code: 200
22
+ ),
23
+ request: ApiTester::Request.new
24
+ response = bad_endpoint.call base_url: contract.base_url + bad_resource,
25
+ method: method,
26
+ payload: {},
27
+ headers: contract.required_headers
28
+ test = MissingResourceTest.new response,
29
+ {},
30
+ endpoint.not_found_response,
31
+ bad_resource,
32
+ ApiTester::SupportedVerbs::GET
33
+ test.check
34
+ end
35
+ end
36
+
37
+ reports
38
+ end
39
+
40
+ def self.allowed_verbs(endpoint)
41
+ allowances = []
42
+ endpoint.methods.each do |method|
43
+ allowances << method.verb
44
+ end
45
+ allowances.uniq
46
+ end
47
+
48
+ def self.order
49
+ 4
50
+ end
51
+ end
52
+
53
+ # Test layout for Missing Resource
54
+ class MissingResourceTest < MethodCaseTest
55
+ def initialize(response, payload, expected_response, url, verb)
56
+ super response: response,
57
+ payload: payload,
58
+ expected_response: expected_response,
59
+ url: url,
60
+ verb: verb,
61
+ module_name: 'Missing Resource'
62
+ end
63
+ end
64
+ end
@@ -26,8 +26,7 @@ end
26
26
 
27
27
  # Report used by module
28
28
  class ServerBroadcastReport
29
- attr_accessor :server_info
30
- attr_accessor :server_key
29
+ attr_accessor :server_info, :server_key
31
30
 
32
31
  def initialize(server_info, server_key)
33
32
  self.server_info = server_info
@@ -9,7 +9,9 @@ module ApiTester
9
9
  def self.go(contract)
10
10
  reports = []
11
11
 
12
- contract.endpoints.each do |endpoint|
12
+ # Filtering out endpoints with ids since not a better way to check this
13
+ # Need to redesign system to handle this better
14
+ contract.endpoints.reject { |e| e.relative_url.include?('{') }.each do |endpoint|
13
15
  allowances(endpoint).each do
14
16
  reports.concat check_typo_url(contract.base_url, endpoint)
15
17
  end
@@ -10,7 +10,7 @@ module ApiTester
10
10
 
11
11
  contract.endpoints.each do |endpoint|
12
12
  endpoint.methods.each do |method|
13
- method.expected_response.body.each do |field|
13
+ method.expected_response.body.filter(&:has_key).each do |field|
14
14
  next unless field.is_seen.zero?
15
15
 
16
16
  reports << MissingFieldReport.new(url: endpoint.url,
@@ -31,14 +31,15 @@ module ApiTester
31
31
  end
32
32
 
33
33
  def print
34
+ puts ''
34
35
  if reports.size.zero?
35
36
  puts 'No issues found'
36
37
  else
37
38
  puts "Issues discovered: #{reports.size}"
38
39
  reports.each do |report|
39
40
  report.print
40
- puts '\n'
41
- puts '\n'
41
+ puts ''
42
+ puts ''
42
43
  end
43
44
  puts "Total issues: #{reports.size}"
44
45
  end
@@ -3,10 +3,7 @@
3
3
  module ApiTester
4
4
  # Report used for when response is missing a field
5
5
  class MissingFieldReport
6
- attr_accessor :url
7
- attr_accessor :verb
8
- attr_accessor :expected_field
9
- attr_accessor :description
6
+ attr_accessor :url, :verb, :expected_field, :description
10
7
 
11
8
  def initialize(url:, verb:, expected_field:, description:)
12
9
  self.url = url
@@ -3,11 +3,7 @@
3
3
  module ApiTester
4
4
  # Standard report format for differing responses
5
5
  class Report
6
- attr_accessor :description
7
- attr_accessor :url
8
- attr_accessor :request
9
- attr_accessor :expected_response
10
- attr_accessor :actual_response
6
+ attr_accessor :description, :url, :request, :expected_response, :actual_response
11
7
 
12
8
  def initialize(description:, url:, request:, expected_response:, actual_response:)
13
9
  self.description = description
@@ -22,7 +18,7 @@ module ApiTester
22
18
  puts " Requested #{url} with payload:"
23
19
  puts " #{request.to_json}"
24
20
  puts ' Expecting: '
25
- puts ' ' + expected_response.to_s
21
+ puts " #{expected_response}"
26
22
  puts ' Receiving: '
27
23
  puts " #{actual_response}"
28
24
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApiTester
4
+ # Report used for when response took too long
5
+ class ResponseTimeReport
6
+ attr_accessor :url, :verb, :payload, :max_time, :actual_time, :description
7
+
8
+ def initialize(url:, verb:, payload:, max_time:, actual_time:, description:)
9
+ self.url = url
10
+ self.verb = verb
11
+ self.payload = payload
12
+ self.max_time = max_time
13
+ self.actual_time = actual_time
14
+ self.description = description
15
+ end
16
+
17
+ def print
18
+ puts "#{description}:"
19
+ puts " #{verb} #{url} took #{actual_time}ms, the max time is #{max_time}ms:"
20
+ puts ' Payload:'
21
+ puts " #{payload}"
22
+ end
23
+ end
24
+ end
@@ -5,8 +5,7 @@ require 'api-tester/reporter/report'
5
5
  module ApiTester
6
6
  # Report for when status code is different than expected
7
7
  class StatusCodeReport < Report
8
- attr_accessor :expected_status_code
9
- attr_accessor :actual_status_code
8
+ attr_accessor :expected_status_code, :actual_status_code
10
9
 
11
10
  def initialize(description:, url:, request:, expected_status_code:, actual_status_code:)
12
11
  super description: description,
@@ -3,6 +3,8 @@
3
3
  module ApiTester
4
4
  # Interface for when things need to be done before or after an api call
5
5
  class TestHelper
6
+ def initialize(url); end
7
+
6
8
  def before; end
7
9
 
8
10
  def retrieve_param(key); end
@@ -3,12 +3,12 @@
3
3
  module ApiTester
4
4
  # Class for evaluating responses against what is expected
5
5
  class ResponseEvaluator
6
- attr_accessor :response_body
7
- attr_accessor :expected_response
6
+ attr_accessor :response_body, :expected_response, :expected_fields_hash
8
7
 
9
8
  def initialize(actual_body:, expected_fields:)
10
9
  self.response_body = actual_body
11
10
  self.expected_response = expected_fields
11
+ self.expected_fields_hash = expected_field_array(expected_response.body)
12
12
  end
13
13
 
14
14
  def response_field_array
@@ -29,10 +29,6 @@ module ApiTester
29
29
  seen
30
30
  end
31
31
 
32
- def expected_fields_hash
33
- expected_field_array expected_response.body
34
- end
35
-
36
32
  def extra_fields
37
33
  response_field_array - expected_fields
38
34
  end
@@ -44,9 +40,14 @@ module ApiTester
44
40
  def expected_field_array(expected_fields)
45
41
  fields = {}
46
42
  expected_fields.each do |field|
47
- fields[field.name] = field
43
+ field_name = field.name
44
+ if field.has_key
45
+ fields[field.name] = field
46
+ else
47
+ field_name = field.type
48
+ end
48
49
  fields = fields.merge inner_expected_field(expected_fields: field.fields,
49
- name: field.name)
50
+ name: field_name)
50
51
  end
51
52
  fields
52
53
  end
@@ -55,7 +56,11 @@ module ApiTester
55
56
  fields = {}
56
57
  expected_fields.each do |field|
57
58
  inner_name = "#{name}.#{field.name}"
58
- fields[inner_name] = field
59
+ if field.has_key
60
+ fields[inner_name] = field
61
+ else
62
+ inner_name = "#{name}.#{field.type}"
63
+ end
59
64
  fields = fields.merge inner_expected_field(expected_fields: field.fields,
60
65
  name: inner_name)
61
66
  end
@@ -65,21 +70,31 @@ module ApiTester
65
70
  def field_array(object)
66
71
  fields = []
67
72
 
68
- object.each do |key, value|
69
- if key.respond_to?('each')
70
- fields.concat(field_array(key))
71
- elsif value == nil || value == 0 || value == false
72
- fields << key.to_s
73
- fields.concat(field_array(value).map { |i| "#{key}.#{i}" })
74
- elsif value.to_s[0] == '[' && value.to_s[-1] == ']' && !value.to_s.include?('=>')
75
- fields << key.to_s
76
- elsif value
77
- fields << key.to_s
78
- fields.concat(field_array(value).map { |i| "#{key}.#{i}" })
79
- else
80
- fields.concat(field_array(key))
73
+ if object.instance_of?(Array)
74
+ name = 'array'
75
+ fields.concat(field_array(object[0]).map { |i| "#{name}.#{i}" })
76
+ else
77
+ object.each do |key, value|
78
+ if key.respond_to?('each')
79
+ fields.concat(field_array(key))
80
+ elsif value == nil || value == 0 || value == false
81
+ fields << key.to_s
82
+ fields.concat(field_array(value).map { |i| "#{key}.#{i}" })
83
+ elsif value.to_s[0] == '[' && value.to_s[-1] == ']' && !value.to_s.include?('=>')
84
+ fields << key.to_s
85
+ elsif value
86
+ passed_value = value
87
+ fields << key.to_s
88
+ if value.instance_of?(Array)
89
+ passed_value = value[0]
90
+ end
91
+ fields.concat(field_array(passed_value).map { |i| "#{key}.#{i}" })
92
+ else
93
+ fields.concat(field_array(key))
94
+ end
81
95
  end
82
96
  end
97
+
83
98
  fields
84
99
  rescue NoMethodError
85
100
  fields
@@ -12,8 +12,8 @@ module ApiTester
12
12
  @hash[key]
13
13
  end
14
14
 
15
- def self.each
16
- @hash.each { |key, value| yield(key, value) }
15
+ def self.each(&block)
16
+ @hash.each(&block)
17
17
  end
18
18
 
19
19
  def self.all
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ApiTester
4
- VERSION = '1.1.1'
4
+ VERSION = '1.1.2'
5
5
  end