Testing the Tests with RSpec
Inferno supports RSpec for automated unit testing of Inferno code. RSpec a high level testing framework that comes with its own DSL, mocking, and web service stubbing. RSpec ensures robustness and quality assurance in Test Kits, especially when they get large and complex.
Running RSpec
Inferno sets up Rake to trigger testing by default
(bundle exec rake
). However using RSpec directly gives more precise control over
the unit tests, such as only running the latest failure. Rspec without Rake can
only be invoked from the Test Kit root directory:
Run Rspec tests randomly:
bundle exec rspec
Run unit tests until the next failure with seed 42:
bundle exec rspec -n -s 42
See bundle exec rspec --help
for all options.
Writing RSpec for an Inferno Server Tests
When Inferno is testing a FHIR server, it acts as a FHIR Client simulator. RSpec then acts as a unit tester on the client simulator. Given the Inferno test code below:
module ExampleTestKit
class ExampleServerSuite < Inferno::TestSuite
id 'example_server_suite'
input :url
input :bearer_token
input :encounter_id
fhir_client do
url :url
bearer_token :bearer_token
end
fhir_resource_validator do
end
group do
id :encounter_group
test do
id :read
title 'Read Encounter'
makes_request :encounter_read
run do
fhir_read(:encounter, encounter_id, name: :encounter_read)
assert_response_status(200)
assert_resource_type(FHIR::Encounter)
end
end
test do
id :validate
title 'Validate Encounter Resource'
uses_request :encounter_read
run do
assert_valid_resource
end
end
end
end
end
The RSpec unit tests need to mock the inputs, the FHIR server endpoints, the Inferno Validator endpoints,
and the FHIR resource in question. Inferno core will auto-inject a
shared context
with helpers when writing a spec for a runnable (Test
, TestGroup
, or TestSuite
).
require 'example_test_kit/example_server_suite'
RSpec.describe ExampleTestKit::ExampleServerSuite do
let(:suite_id) { 'example_server_suite' } # This is always required by Inferno Core's shared context
let(:url) { 'https://fhir.example.com' }
let(:bearer_token) { 'dummy_token' }
let(:encounter_id) { '123' }
let(:encounter_fixture_json) do
File.read('spec/fixtures/encounter.json')
end
describe 'encounter group' do
describe 'read test' do
let(:test) { find_test(suite, 'read') } # `find_test` and `suite` are from Inferno Core
# `find_test` will load the test its parents inputs
# `described_class` will load the test with only its own inputs
it 'passes if an Encounter was received' do
stub_request(:get, "#{url}/Encounter/#{encounter_id}") # Stub FHIR server endpoint
.to_return(status: 200, body: encounter_fixture_json)
result = run(test, { url:, bearer_token:, encounter_id: }, {}) # `run(runnable, inputs, scratch)` is from Inferno Core
expect(result.result).to eq('pass'), result.result_message
end
it 'fails if a Patient was recieved' do
stub_request(:get, "#{url}/Encounter/#{encounter_id}")
.to_return(status: 200, body: FHIR::Patient.new.to_json)
result = run(test, { url:, bearer_token:, encounter_id: })
expect(result.result).to eq('fail'), result.result_message
end
end
describe 'validate test' do
let(:test) { find_test(suite, 'validate') }
let(:validation_success) do # stubbed response from Inferno Validator
{
outcomes: [{
issues: []
}],
sessionId: test_session.id # `test_session` is from Inferno Core
}.to_json
end
it 'passes if the resource is valid' do
stub_request(:post, validation_url) # `validation_url` is from Inferno Core
.to_return(status: 200, body: validation_success)
repo_create( # `repo_create` is from Inferno Core
:request,
name: :encounter_read, # stub for `uses_request`
test_session_id: test_session.id,
response_body: encounter_fixture_json
)
result = run(test, { url:, bearer_token:, encounter_id: })
expect(result.result).to eq('pass'), result.result_message
end
end
end
end
If writing a spec for a non-runnable class that needs the Inferno
Core helpers, add :runnable
to the RSpec metadata. This is common
for modules that are imported into Inferno tests:
class MyTest < Inferno::Test
include MyModule
# ... Inferno test code ...
end
RSpec.describe MyModule, :runnable do
# ... RSpec test code ...
end
Writing RSpec for Inferno Client Tests
When Inferno tests a FHIR client, it acts a FHIR server simulator. RSpec is therefore unit testing a server. Given the Inferno test code blow:
module ExampleTestKit
class EncounterEndpoint < Inferno::DSL::SuiteEndpoint
def test_run_identifier
request.headers['authorization']&.delete_prefix('Bearer ')
end
def make_response
response.status = 200
response.body = FHIR::Encounter.new(id: request.params[:id]).to_json
response.format = :json
end
def update_result
results_repo.update(result.id, result: 'pass')
end
def tags
['encounter_request']
end
end
class ExampleClientSuite < Inferno::TestSuite
id :example_client_suite
input :bearer_token
suite_endpoint :get, '/Encounter/:id', EncounterEndpoint
group do
id :client_group
test do
id :retrieve
title 'Retrieve Encounter Resource'
run do
assert load_tagged_requests('encounter_request').any?
end
end
end
end
end
the RSpec test code for it is:
require 'example_test_kit/example_client_suite'
RSpec.describe ExampleTestKit::ExampleClientSuite do
let(:suite_id) { 'example_client_suite' }
let(:bearer_token) { 'dummy_token' }
describe 'retrieve encounter test' do
let(:test) { find_test(suite, 'retrieve') }
it 'passes after read encounter request received' do
allow(test).to receive_messages(suite:)
result = run(test, bearer_token:)
expect(result.result).to eq('fail') # Assert test is fail before Encounter request
repo_create( # Mock the Encounter retrieval request
:request,
verb: 'get',
url: 'https://fhir.example.com/Encounter/123',
direction: 'incoming',
result_id: result.id,
test_session_id: test_session.id,
tags: ['encounter_request']
)
result = run(test, bearer_token:) # Re-run test now that Encounter request was mocked
expect(result.result).to eq('pass') # Assert test is pass after Encounter request
end
end
end
RSpec for Inferno Platform Test Kits
Test Kits that will be deployed on an Inferno Platform should conform to the platform deployable test kit spec. This spec is a set of shared RSpec examples which can be imported via:
RSpec.describe ExampleTestKit, order: :defined do
it_behaves_like 'platform_deployable_test_kit'
end
Suggest an improvement
Want to make an change? Contribute an edit for this page on the Inferno Framework GitHub repository.