Collection of RSpec matchers for your API.
Response Body Matchers
have_node
have_json_node
have_xml_node
have_json
Response Status Matchers
be_ok
create_resource
be_a_bad_request
be_unauthorized
be_forbidden
be_internal_server_error
be_not_found
Other Matchers
be_in_xml
be_in_json
Install
Include the gem to your test group in you Gemfile:
group :test do
gem 'api_matchers'
# other gems
end
Or install it manually: gem install api_matchers
.
Usage
Including in RSpec
To include all this matchers you need to include the APIMatchers::RSpecMatchers module:
RSpec.configure do |config|
config.include APIMatchers::RSpecMatchers
end
Have Node Matcher
The have_node matcher parse the actual and see if have the expcted node with the expected value. The default that have_node will parse is JSON.
You can verify if node exists:
expect('{ "transaction": { "id": 54, "status": "paid" } }').to have_node(:transaction)
Or if node exist with a value:
expect('{ "transaction": { "id": 54, "status": "paid" } }').to have_node(:id).with(54)
expect('{ "error": "not_authorized" }').to have_node(:error).with('not_authorized')
expect('{"parcels":1 }').to have_node(:parcels).with(1)
To see the json node and see if include a text, you can do this:
expect('{"error": "Transaction error: Name cant be blank"}').to have_node(:error).including_text("Transaction error")
You can verify boolean values too:
expect('{"creditcard":true}').to have_node(:creditcard).with(true)
HAVE NODE Matcher Configuration
You can configure if you want xml (JSON is the default):
APIMatchers.setup do |config|
config.content_type = :xml
end
expect('<transaction><id>200</id><status>paid</status></transaction>').to have_node(:status)
Using the with
method:
expect('<transaction><id>200</id><status>paid</status></transaction>').to have_node(:status).with('paid')
Or you can use the have_xml_node
matcher:
expect(
"<error>Transaction error: Name can't be blank</error>"
).to have_xml_node(:error).with("Transaction error: Name can't be blank")
To see the xml node and see if include a text, you can do this:
expect(
"<error>Transaction error: Name can't be blank</error>"
).to have_xml_node(:error).including_text("Transaction error")
If you work with xml and json in the same API, check the have_json_node and have_xml_node matchers.
You can configure the name of the method and then you will be able to use without the #body method, for example:
APIMatchers.setup do |config|
config.response_body_method = :body
end
expect(response).to have_node(:foo).with('bar')
Instead of:
expect(response.body).to have_node(:foo)
Have JSON Node Matcher
expect(
'{ "transaction": { "id": 54, "status": "paid" } }'
).to have_json_node(:id).with(54)
Have XML Node Matcher
expect("<product><name>gateway</name></product>").to have_xml_node(:name).with('gateway')
Have JSON Matcher
Sometimes, you want to compare the entire JSON structure:
expect("['Foo', 'Bar', 'Baz']").to have_json(['Foo', 'Bar', 'Baz'])
Create Resource Matcher
This matchers see the HTTP STATUS CODE is equal to 201.
expect(response.status).to create_resource
BAD REQUEST Matcher
This BAD REQUEST is a matcher that see if the HTTP STATUS code is equal to 400.
expect(response.status).to be_a_bad_request
expect(response.status).to be_bad_request
UNAUTHORIZED Matcher
This UNAUTHORIZED is a matcher that see if the HTTP STATUS code is equal to 401.
expect(response.status).to be_unauthorized
expect(response.body).to have_node(:message).with('Invalid Credentials')
FORBIDDEN Matcher
This is a matcher to see if the HTTP STATUS code is equal to 403.
expect(response.status).to be_forbidden
INTERNAL SERVER ERROR Matcher
This INTERNAL SERVER Error is a matcher that see if the HTTP STATUS code is equal to 500.
expect(response.status).to be_internal_server_error
expect(
response.body
).to have_node(:message).with('An Internal Error Occurs in our precious app. :S')
HTTP STATUS CODE Configuration
You can configure the name method to call the http status code:
APIMatchers.setup do |config|
config.http_status_method = :status
end
Then you can use without call the #status method:
expect(response).to create_resource
This configurations affects this matchers:
be_ok
create_resource
be_a_bad_request
be_internal_server_error
be_unauthorized
be_forbidden
be_not_found
Be in XML Matcher
This is a matcher that see if the content type is xml:
expect(response.headers['Content-Type']).to be_in_xml
Be in JSON Matcher
This is a matcher that see if the content type is in JSON:
expect(response.headers['Content-Type']).to be_in_json
Headers Configuration
You can configure the name method to call the headers and content type:
APIMatchers.setup do |config|
config.header_method = :headers
config.header_content_type_key = 'Content-Type'
end
And then you will be able to use without call the #headers calling the #['Content-Type'] method:
expect(response).to be_in_json
expect(response).to be_in_xml
Acknowlegments
- Special thanks to Daniel Konishi to contribute in the product that I extracted the matchers to this gem.
Contributors
- Stephen Orens
- Lucas Caton