Refactor Specs to Use Newer RSpec Features
Based on this draft -> reviewed patchset (https:/
This refactoring happens in roughly this order:
1. Convert the before() blocks to use let() blocks instead
2. Convert the it() blocks to use the new chef_run method instead of @chef_run variable
3. Remove duplicate before() blocks from it() blocks, and leaving any requisite node setups for that test
4. Convert methods with shared setup (image stubs) in spec_helper to shared_
5. Convert methods with shared tests in spec_helper to shared_
6. Cleanup and unnecessary use if :: in Class/Variable usages and () methods as necessary
More details about each step included below. You can also find the latest RSpec documentation here: https:/
-------
1. Converting before blocks to let blocks
-------
Because the current before blocks include code that takes a fixed path, it is necessary to duplicate it when need in specific tests to configure test specific node settings. For example:
describe 'ubuntu' do
before do
@chef_run = ::ChefSpec:
end
@
end
at the top of the specs, then later in the file:
it 'has configurable default_store setting for swift' do
chef_run = ::ChefSpec:
end
chef_
expect(
end
Now we have duplicate setup code and we're going to run two converges, slowing things down. To combat this, we break the setup code into smaller chunks using the let() syntax. Any block can be called in any order in tests.
describe 'ubuntu' do
let(:runner) { ChefSpec:
let(:node) { runner.node }
let(:chef_run) do
node.
runner.
end
Given the code above, calling :chef_run will instantiate OR reuse :node (from :runner), which will instantiate or reuse :runner, then run converge. No matter how many times we call any of those, they only happen onces and are memoized er spec. Also take note of the described_recipe. We don't need to hard code the recipe name, but rather use the one declared at the very top of the spec file.
Now we can simply call node in our test above before cvalling chef_run without the duplicated setup:
it 'has configurable default_store setting for swift' do
node.
expect(
end
On more complex setups where we need to also tweak the UBUNTU_OPTS to instruct ChefSpec to descend into an lwrp, we can juse do this in the specific spec files:
describe 'ubuntu' do
let(:runner) { ChefSpec:
let(:options) { UBUNTU_
We can even alter these now per test if we have to:
it 'has configurable default_store setting for swift' do
options.
node.
expect(
end
or for the initiated:
describe 'ubuntu' do
let(:runner) { ChefSpec:
let(:options) { UBUNTU_
let(:lwrps) { ['my_lwrp'] }
Order of the lets() do not matter as they're execute on an as called/as needed basis.
-------
2. Converting @chef_run to chef_run in specs
-------
This is simply a matter of calling the chef_run method rather than using the @chef_run instance variable:
it 'has configurable default_store setting for swift' do
expect(
end
it 'has configurable default_store setting for swift' do
expect(
end
Note: the parentheses around the upgrade_package method call for better readability.
-------
3. Remove duplicate before blocks
-------
As covered by #1 above, now that we have top_level chef_run and node objects at our disposal, we can use them directly in specs while removing the setup/before configurations:
it 'has configurable default_store setting for swift' do
chef_run = ::ChefSpec:
end
chef_
expect(
end
becomes:
it 'has configurable default_store setting for swift' do
node.
expect(
end
-------
4/5 Convert methods in spec_helper to shared_
-------
Some of the spec_helper.rb files have methods that contain specs that are reused across several recipes.
Rubocop complains about these (complexity/# of lines) and they're not really methods, but shared examples. So, we convert them to the RSpec shared_
While the difference between shared_context and shared_examples is hard to parse and they can most times be used interchangeably, the general rule of thumbs seems to be:
If you're sharing mostly setup/before stuff: use shared_context
If you're sharing examples, or want to say "test that this thing behaves like that thing", use shared_examples
So, in the image spec_helpers:
def image_stubs # rubocop:disable MethodLength
::Chef:
.with('lo')
.and_
::Chef:
.with(
.and_return(
'host' => 'rabbit-host', 'port' => 'rabbit-port'
)
::Chef:
::Chef:
.with(
.and_
::Chef:
.with(
.and_
::Chef:
end
becomes a shared_context:
shared_context 'image-stubs' do
before do
::Chef:
.with('lo')
.
::Chef:
.
.and_return(
'host' => 'rabbit-host', 'port' => 'rabbit-port'
)
::Chef:
::Chef:
.
.
::Chef:
.
.
::Chef:
end
end
And to use it, we simple do this in our spec files:
include_context 'image-stubs'
rather than:
describe 'openstack-
before { image_stubs }
These can be broken out into require-able files in spec/support if necessary.
The process is nearly identical for shared_examples: In spec_helper:
def expect_
describe '/etc/glance' do
before do
@dir = @chef_run.directory '/etc/glance'
end
it 'has proper owner' do
end
it 'has proper modes' do
end
end
end
becomes:
shared_examples 'glance-directory' do
describe '/etc/glance' do
let(:dir) { chef_run.
it 'has proper owner' do
expect(
expect(
end
it 'has proper modes' do
expect(
end
end
end
Take note that in this example, we also have converted the before block to match the let() style and added () around the directory call.
Now in our specs, we simply do:
include_examples 'glance-directory'
-------
6 Remove unneeded :: in Class/Variable usages
-------
:: is the ruby hint to tell it to start at the root of the namespace rather than use the current [nested] namespace when doing variable/class lookups.
Since the likelyhood of us having two variables/classes named REDHAT_OPTS and we don't have a nested class named Chef::Recipe, it's pretty safe to just remove all of these to readability sake.
::UBUNTU_OPTS = {} becomse UBUNTU_OPTS = {}
::Chef:
etc, etc
Blueprint information
- Status:
- Complete
- Approver:
- Justin Shepherd
- Priority:
- Medium
- Drafter:
- Christopher H. Laco
- Direction:
- Approved
- Assignee:
- Justin Shepherd
- Definition:
- Approved
- Series goal:
- Accepted for icehouse
- Implementation:
- Implemented
- Milestone target:
- icehouse-stable
- Started by
- Justin Shepherd
- Completed by
- Justin Shepherd
Related branches
Related bugs
Sprints
Whiteboard
Gerrit topic: https:/
Addressed by: https:/
refactor chefspec tests to be faster and more efficient
Addressed by: https:/
refactor specs to improve speed and maintenance
Addressed by: https:/
Refactoring chefspec tests
Addressed by: https:/
Refactoring chefspec tests
Addressed by: https:/
Refactoring ChefSpec tests
Addressed by: https:/
refactor chefspec tests to be cleaner and faster
Addressed by: https:/
Refactoring ChefSpec tests
Addressed by: https:/
Refactoring ChefSpec tests
Addressed by: https:/
Refactor ChefSpec tests
Addressed by: https:/
Refactor ChefSpec tests
Addressed by: https:/
Refactoring ChefSpec for identity_register lwrp
Work Items
Work items:
Convert specs in stackforge/
Convert specs in stackforge/
Convert specs in stackforge/
Convert specs in stackforge/
Convert specs in stackforge/
Convert specs in stackforge/
Convert specs in stackforge/
Convert specs in stackforge/
Convert specs in stackforge/
Convert specs in stackforge/
Convert specs in stackforge/