I have the following spec test:
require 'swagger_helper'
describe 'Devices API' do
path '/api/v1/devices' do
get 'Retrieves devices, optionally mating criteria' do
security [ Token: [] ]
produces 'application/json'
let(:api_key) { FactoryGirl.create(:api_key) }
let(:Authorization) { "Token token=\"#{api_key.access_token}\"" }
before do |example|
loc = FactoryGirl.create(:location, name: 'Earth')
FactoryGirl.create(:device, location: loc)
FactoryGirl.create(:device, location: loc)
FactoryGirl.create(:device, location: loc)
submit_request(example.metadata)
end
response '200', 'Returns all Earth devices' do
parameter name: :location, :in => :query, :type => :string
let(:location) { 'Earth' }
it 'returns a valid 201 response' do |example|
assert_response_matches_metadata(example.metadata)
end
it 'returns an array with 3 entries' do |example|
data = JSON.parse(response.body)
p data
expect(data.class).to be(Array)
expect(data.size).to eq(3)
end
end
response '200', 'no devices found' do
parameter name: :role, :in => :query, :type => :string
let(:role) { 'DoesNotExist' }
before do |example|
submit_request(example.metadata)
end
examples 'application/json' => {}
it 'returns an array with 0 entries' do |example|
data = JSON.parse(response.body)
expect(data.class).to be(Array)
expect(data.size).to eq(0)
end
end
end
end
end
I'm failing to understand how parameters work in this case. My api method call takes three optional parameters that I would like to test independently, but in each response block, if I add a parameter to it, that parameter seems to be required in all other response blocks. So if a parameter shows up in any response block, its appended to the call for every response block., which is not what I want.
I'm looking for a way that I can independently test a parameter in isolation, but have all parameters noted in the API docs (and not duplicated). If anyone has advice on the subject, I would appreciate it.
You need to describe the operation fully, including optional parameters, before the response blocks. Unlike the declarations that come before it, a response block is a specific test case where you provide an expected result (e.g. status code) and then assign parameter values (via let) to yield that result. There should be only 1 response block per status code but you can use contexts within them to create sub test cases for a given status code. For example:
get 'Retrieves devices, optionally mating criteria' do
produces 'application/json'
parameter name: :location, in: :query, type: 'string', required: false
parameter role: :location, in: :query, type: 'string', required: false
response '200', 'success' do
context 'location provided' do
let(:location) { 'Earth' }
before do
submit_request(example.metadata)
end
it 'returns devices matching the provided location' do
data = JSON.parse(response.body)
expect(data.size).to eq(3)
end
end
context 'role provided' do
let(:role) { 'DoesNotExist' }
before do
submit_request(example.metadata)
end
it 'returns devices matching the provided role' do
data = JSON.parse(response.body)
expect(data.size).to eq(0)
end
end
end
end
Thanks for the response. I'd initially thought that's how it worked, but I keep running into an issue. Here is an updated spec and the failure I see. Note the commented let items are those throwing the rspec errors below. I've also removed bits from the test you likely don't care about.
get 'Retrieves devices, optionally mating criteria' do
produces 'application/json'
parameter name: :location, in: :query, type: 'string', required: false
parameter name: :model, in: :query, type: 'string', required: false
parameter name: :role, in: :query, type: 'string', required: false
response '200', 'Returns all Earth devices' do
before do |example|
submit_request(example.metadata)
end
context 'location provided' do
let(:location) { 'Earth' }
# let(:model) { 'Human' }
# let(:role) { 'Think' }
it 'returns an array with 3 entries' do |example|
data = JSON.parse(response.body)
expect(data.class).to be(Array)
expect(data.size).to eq(3)
end
end
end
end
The failure for this test is as follows.
1) Devices API /devices get Returns all Earth devices location provided returns an array with 3 entries
Failure/Error: submit_request(example.metadata)
NoMethodError:
undefined method `model' for #<RSpec::ExampleGroups::DevicesAPI::Devices::Get::ReturnsAllEarthDevices::LocationProvided:0x007f88c44469a8>
# ./spec/integration/devices_spec.rb:28:in `block (5 levels) in <top (required)>'
# ./spec/spec_helper.rb:80:in `block (3 levels) in <top (required)>'
# ./spec/spec_helper.rb:79:in `block (2 levels) in <top (required)>'
If I instead comment out the let for location, I get a different error.
1) Devices API /devices get Returns all Earth devices location provided returns an array with 3 entries
Failure/Error: submit_request(example.metadata)
`location` is not available from within an example (e.g. an `it` block) or from constructs that run in the scope of an example (e.g. `before`, `let`, etc). It is only available on an example group (e.g. a `describe` or `context` block).
# ./spec/integration/devices_spec.rb:28:in `block (5 levels) in <top (required)>'
# ./spec/spec_helper.rb:80:in `block (3 levels) in <top (required)>'
# ./spec/spec_helper.rb:79:in `block (2 levels) in <top (required)>'
Which version of rswag? You'll need latest - 1.5.0
Ah, much better. Updating to 1.5.0 has corrected both rspec errors above. Thank you.
Most helpful comment
You need to describe the operation fully, including optional parameters, before the
responseblocks. Unlike the declarations that come before it, aresponseblock is a specific test case where you provide an expected result (e.g. status code) and then assign parameter values (via let) to yield that result. There should be only 1responseblock per status code but you can usecontextswithin them to create sub test cases for a given status code. For example: