Skip to content

Commit baa41f0

Browse files
committed
New matchers for status, content_type, page links
Also rename matchers/matchers to matchers/dsl for clarity and require all the matchers to be called on a response object
1 parent 99d8e95 commit baa41f0

File tree

14 files changed

+262
-95
lines changed

14 files changed

+262
-95
lines changed

Gemfile

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
11
source 'https://rubygems.org'
22

3-
gem 'debugger'
4-
53
gemspec

Gemfile.lock

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,12 @@ PATH
88
GEM
99
remote: https://rubygems.org/
1010
specs:
11-
columnize (0.3.6)
1211
coveralls (0.7.0)
1312
multi_json (~> 1.3)
1413
rest-client
1514
simplecov (>= 0.7)
1615
term-ansicolor
1716
thor
18-
debugger (1.6.2)
19-
columnize (>= 0.3.1)
20-
debugger-linecache (~> 1.2.0)
21-
debugger-ruby_core_source (~> 1.2.3)
22-
debugger-linecache (1.2.0)
23-
debugger-ruby_core_source (1.2.3)
2417
diff-lcs (1.2.4)
2518
mime-types (2.0)
2619
multi_json (1.8.2)
@@ -51,6 +44,5 @@ PLATFORMS
5144
DEPENDENCIES
5245
bundler (~> 1.3)
5346
coveralls
54-
debugger
5547
rake
5648
rspec-api-matchers!

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ RSpec API Matchers
33

44
RSpecApi::Matchers lets you express outcomes on the response of web APIs.
55

6-
expect(404).to match_status(:not_found)
6+
expect(response).to have_status(:not_found)
77

88
More documentation and examples about RSpec Api are available at [http://rspec-api.github.io](http://rspec-api.github.io)
99

10-
[![Build Status](https://travis-ci.org/rspec-api/rspec-api-matchers.png)](https://travis-ci.org/rspec-api/rspec-api-matchers)
10+
[![Build Status](https://travis-ci.org/rspec-api/rspec-api-matchers.png?branch=master)](https://travis-ci.org/rspec-api/rspec-api-matchers)
1111
[![Code Climate](https://codeclimate.com/github/rspec-api/rspec-api-matchers.png)](https://codeclimate.com/github/rspec-api/rspec-api-matchers)
1212
[![Coverage Status](https://coveralls.io/repos/rspec-api/rspec-api-matchers/badge.png)](https://coveralls.io/r/rspec-api/rspec-api-matchers)
1313
[![Dependency Status](https://gemnasium.com/rspec-api/rspec-api-matchers.png)](https://gemnasium.com/rspec-api/rspec-api-matchers)
@@ -21,7 +21,9 @@ Or install yourself by running `gem install rspec-api-matchers`.
2121
Available matchers
2222
------------------
2323

24-
expect(http_status_code).to match_status(http_status)
24+
expect(response).to have_status(:ok)
25+
expect(response).to include_content_type(:json)
26+
expect(response).to have_prev_page_link
2527

2628
How to contribute
2729
=================

lib/rspec-api/matchers.rb

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
require 'rspec-api/matchers/version'
2-
require 'rspec-api/matchers/matchers'
3-
require 'rspec-api/matchers/match_status'
2+
require 'rspec-api/matchers/dsl'
3+
4+
require 'rspec-api/matchers/status'
5+
require 'rspec-api/matchers/content_type'
6+
require 'rspec-api/matchers/prev_page_link'
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
require 'rack/utils'
2+
3+
module RSpecApi
4+
module Matchers
5+
class ContentType
6+
def initialize(content_type)
7+
@content_type = content_type
8+
end
9+
10+
def matches?(response)
11+
@headers = response.headers
12+
if @headers.key? 'Content-Type'
13+
@headers['Content-Type'] == @content_type
14+
end
15+
end
16+
alias == matches?
17+
18+
def failure_message_for_should
19+
"expected headers to #{description}, but got #{@headers}"
20+
end
21+
22+
def failure_message_for_should_not
23+
"expected headers not to #{description}, but was found"
24+
end
25+
26+
def description
27+
"include 'Content-Type': '#{@content_type}'"
28+
end
29+
end
30+
end
31+
end

lib/rspec-api/matchers/dsl.rb

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
module RSpecApi
2+
module Matchers
3+
# Convert RSpecAPI::Matchers classes into RSpec-compatible matchers, e.g.:
4+
# makes RSpecApi::Matchers::Status available as expect(...).to match_status
5+
module DSL
6+
# Passes if response has the given HTTP status code.
7+
#
8+
# @example
9+
#
10+
# # Passes if the status is 200 OK (passed as a symbol)
11+
# expect(OpenStruct.new status: 200).to match_status :ok
12+
#
13+
# # Passes if the status is 200 OK (passed as a number)
14+
# expect(OpenStruct.new status: 200).to match_status 200
15+
def have_status(status)
16+
RSpecApi::Matchers::Status.new status
17+
end
18+
19+
# Passes if response includes the given Content-Type JSON in the headers
20+
#
21+
# @example
22+
#
23+
# # Passes if the headers specify that Content-Type is JSON
24+
# headers ={'Content-Type' => 'application/json; charset=utf-8'}
25+
# expect(OpenStruct.new headers: headers).to include_content_type(:json)
26+
def include_content_type(type = nil)
27+
content_type = case type
28+
when :json then 'application/json; charset=utf-8'
29+
end
30+
RSpecApi::Matchers::ContentType.new content_type
31+
end
32+
33+
# Passes if response includes the pagination Link rel=prev in the headers
34+
#
35+
# @example
36+
#
37+
# # Passes if the headers specify the pagination link for Prev
38+
# headers = {'Link' => '<https://example.com/1>; rel="prev"'}
39+
# expect(OpenStruct.new headers: headers).to have_prev_page_link
40+
def have_prev_page_link
41+
RSpecApi::Matchers::PrevPageLink.new
42+
end
43+
end
44+
end
45+
end
46+
47+
require 'rspec/matchers'
48+
module RSpec
49+
module Matchers
50+
# Make RSpecApi::Matchers::DSL methods available inside RSpec
51+
include ::RSpecApi::Matchers::DSL
52+
end
53+
end

lib/rspec-api/matchers/matchers.rb

Lines changed: 0 additions & 26 deletions
This file was deleted.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
require 'rack/utils'
2+
3+
module RSpecApi
4+
module Matchers
5+
class PrevPageLink
6+
def matches?(response)
7+
@headers = response.headers
8+
links = @headers['Link'] || '' # see http://git.io/CUz3-Q
9+
links =~ %r{<.+?>. rel\="prev"}
10+
end
11+
alias == matches?
12+
13+
def failure_message_for_should
14+
"expected headers to #{description}, but got #{@headers}"
15+
end
16+
17+
def failure_message_for_should_not
18+
"expected headers not to #{description}, but one was found"
19+
end
20+
21+
def description
22+
%Q{include a link to the previous page}
23+
end
24+
end
25+
end
26+
end

lib/rspec-api/matchers/match_status.rb renamed to lib/rspec-api/matchers/status.rb

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,27 @@
22

33
module RSpecApi
44
module Matchers
5-
class MatchStatus
6-
def initialize(expected_status)
7-
@expected_status = expected_status
5+
class Status
6+
def initialize(status)
7+
@expected_status = status
88
end
99

10-
def matches?(actual_code)
11-
@actual_code = actual_code
12-
actual_code == expected_code
10+
def matches?(response)
11+
@status = response.status
12+
@status == expected_code
1313
end
1414
alias == matches?
1515

1616
def failure_message_for_should
17-
"expected #{@actual_code} to #{description}"
17+
"expected HTTP status code #{expected_code}, got #{@status}"
1818
end
1919

2020
def failure_message_for_should_not
21-
"expected #{@actual_code} not to #{description}"
21+
"expected HTTP status code not to be #{expected_code}, but it was"
2222
end
2323

2424
def description
25-
"be #{@expected_status}"
25+
"be HTTP status code #{expected_code}"
2626
end
2727

2828
private
@@ -36,7 +36,7 @@ def expected_code
3636
# @example
3737
# status_to_code(:ok) # => 200
3838
# status_to_code(200) # => 200
39-
# status_to_code(987) # => raise
39+
# status_to_code(987) # => raise ArgumentError
4040
def status_to_numeric_code(status)
4141
code = status.is_a?(Symbol) ? Rack::Utils.status_code(status) : status
4242
validate_status_code! code
@@ -47,7 +47,7 @@ def status_to_numeric_code(status)
4747
#
4848
# @example
4949
# validate_status_code!(200) # => (no error)
50-
# validate_status_code!(999) # => raise
50+
# validate_status_code!(999) # => raise ArgumentError
5151
def validate_status_code!(code)
5252
valid_codes = Rack::Utils::SYMBOL_TO_STATUS_CODE.values
5353
message = "#{code} must be a valid HTTP status code: #{valid_codes}"
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
require 'spec_helper'
2+
3+
describe 'include_content_type matcher' do
4+
let(:response) { OpenStruct.new headers: {} }
5+
6+
describe 'expect(response).to include_content_type(...)' do
7+
it 'passes if the response headers include the given content type' do
8+
response.headers = {'Content-Type' => 'application/json; charset=utf-8'}
9+
expect(response).to include_content_type(:json)
10+
end
11+
12+
it 'fails if the response headers include a different content type' do
13+
response.headers = {'Content-Type' => 'application/xml; charset=utf-8'}
14+
expect {
15+
expect(response).to include_content_type(:json)
16+
}.to fail_with %r{expected headers to include 'Content-Type': 'application/json; charset=utf-8', but got}
17+
end
18+
19+
end
20+
21+
describe 'expect(response).not_to include_content_type(...)' do
22+
it 'passes if the response headers do not include content type' do
23+
response.status = 204
24+
expect(response).not_to include_content_type
25+
end
26+
27+
it 'fails if the response headers include the given content type' do
28+
response.headers = {'Content-Type' => 'application/json; charset=utf-8'}
29+
expect {
30+
expect(response).not_to include_content_type(:json)
31+
}.to fail_with %q{expected headers not to include 'Content-Type': 'application/json; charset=utf-8', but was found}
32+
end
33+
end
34+
end

0 commit comments

Comments
 (0)