ChefSpec
Используйте ChefSpec для моделирования сходимости ресурсов на узле:
- Является расширением RSpec, фреймворка поведенческого программирования (BDD) для Ruby
- Самый быстрый способ тестирования ресурсов и рецептов
ChefSpec — это фреймворк, который тестирует ресурсы и рецепты в рамках имитируемого выполнения Chef Infra Client. Тесты ChefSpec выполняются очень быстро. При использовании в рамках рабочего процесса создания кулинарных книг, тесты ChefSpec часто являются первым индикатором проблем, которые могут существовать в кулинарной книге.
Запуск ChefSpec
ChefSpec входит в состав Chef Workstation. Для запуска ChefSpec:
chef exec rspec
Тесты единиц
RSpec — это фреймворк поведенческого программирования (BDD), который использует естественный язык предметно-ориентированного языка (DSL) для быстрого описания сценариев, в которых тестируются системы. RSpec позволяет настроить и выполнить сценарий. Результаты сравниваются со множеством определённых ожиданий.
ChefSpec построен на основе RSpec DSL.
Синтаксис
Синтаксис тестов на основе RSpec должен следовать естественным языковым описаниям самого RSpec. Тесты должны создавать предложения, похожие на английские: «Сумма одного плюс один равна двум, а не трём». Например:
describe '1 plus 1' do
it 'equals 2' do
a = 1
b = 1
sum = a + b
expect(sum).to eq(2)
expect(sum).not_to eq(3)
end
end
где:
-
describeсоздает сценарий тестирования:1 plus 1 -
it— это блок, который определяет список параметров для тестирования, а также параметры, которые определяют ожидаемый результат -
describeиitдолжны иметь описания, понятные человеку: «один плюс один равно двум» -
a,b, иsumопределяют сценарий тестирования:aравно одному,bравно одному, сумма одного плюс один равна двум -
expect()определяет ожидание: сумма одного плюс один равна двум—expect(sum).to eq(2)—и не равна трём—expect(sum).not_to eq(3) -
.toпроверяет результаты теста на истинность;.not_toпроверяет результаты теста на ложность; тест проходит, когда результаты теста истинны
context
Тесты на основе RSpec могут содержать context блоки. Используйте context блоки внутри describe блоков для определения «тестов внутри тестов». Каждый context блок тестируется индивидуально. Все context блоки внутри describe блока должны быть истинными для прохождения теста. Например:
describe 'math' do
context 'when adding 1 + 1' do
it 'equals 2' do
expect(sum).to eq(2)
end
end
context 'when adding 2 + 2' do
it 'equals 4' do
expect(sum).to eq(4)
end
end
end
где каждый context блок описывает другой сценарий тестирования: «Сумма одного плюс один равна двум, а также сумма двух плюс двух равна четырём». Блок context полезен для обработки платформенно-специфичных сценариев. Например, «При работе на платформе А, проверьте foo; при работе на платформе B, проверьте bar». Например:
describe 'cookbook_name::recipe_name' do
context 'when on Debian' do
it 'equals 2' do
a = 1
b = 1
sum = a + b
expect(sum).to eq(2)
end
end
context 'when on Ubuntu' do
it 'equals 2' do
expect(1 + 1).to eq(2)
end
end
context 'when on Windows' do
it 'equals 3' do
expect(1 + 2).to eq(3)
end
end
end
let
Тесты на основе RSpec могут содержать let выражения внутри context блока. Используйте let выражения для создания символа, присвоения ему значения и последующего использования в context блоке. Например:
describe 'Math' do
context 'when adding 1 + 1' do
let(:sum) { 1 + 1 }
it 'equals 2' do
expect(sum).to eq(2)
end
end
context 'when adding 2 + 2' do
let(:sum) do
2 + 2
end
it 'equals 4' do
expect(sum).to eq(4)
end
end
end
где:
- Первое
letвыражение создаёт символ:sum, а затем присваивает ему значение одного плюс один. Выражениеexpectпозже в тесте используетsum, чтобы проверить, что один плюс один равно двум - Второе
letвыражение создаёт символ:sum, а затем присваивает ему значение двух плюс двух. Выражениеexpectпозже в тесте используетsum, чтобы проверить, что два плюс два равны четырём
Требование ChefSpec
Тест ChefSpec должен содержать следующее выражение в верхней части файла теста:
require 'chefspec'
Примеры
Репозиторий ChefSpec на github содержит впечатляющую коллекцию примеров. Для всех основных ресурсов Chef Infra Client, для гвардейцев, атрибутов, нескольких действий и т. д. Посмотрите на эти примеры и используйте их в качестве отправной точки для создания собственных тестов. Некоторые из них включены ниже, для справки здесь.
Ресурс file
Рецепт
file '/tmp/explicit_action' do
action :delete
end
file '/tmp/with_attributes' do
user 'user'
group 'group'
backup false
action :delete
end
file 'specifying the identity attribute' do
path '/tmp/identity_attribute'
action :delete
end
Тест единицы
require 'chefspec'
describe 'file::delete' do
let(:chef_run) { ChefSpec::SoloRunner.new(platform: 'ubuntu', version: '20.04').converge(described_recipe) }
it 'deletes a file with an explicit action' do
expect(chef_run).to delete_file('/tmp/explicit_action')
expect(chef_run).to_not delete_file('/tmp/not_explicit_action')
end
it 'deletes a file with attributes' do
expect(chef_run).to delete_file('/tmp/with_attributes').with(backup: false)
expect(chef_run).to_not delete_file('/tmp/with_attributes').with(backup: true)
end
it 'deletes a file when specifying the identity attribute' do
expect(chef_run).to delete_file('/tmp/identity_attribute')
end
end
Ресурс template
Рецепт
template '/tmp/default_action'
template '/tmp/explicit_action' do
action :create
end
template '/tmp/with_attributes' do
user 'user'
group 'group'
backup false
end
template 'specifying the identity attribute' do
path '/tmp/identity_attribute'
end
Тест единицы
require 'chefspec'
describe 'template::create' do
let(:chef_run) { ChefSpec::SoloRunner.new(platform: 'ubuntu', version: '20.04').converge(described_recipe) }
it 'creates a template with the default action' do
expect(chef_run).to create_template('/tmp/default_action')
expect(chef_run).to_not create_template('/tmp/not_default_action')
end
it 'creates a template with an explicit action' do
expect(chef_run).to create_template('/tmp/explicit_action')
end
it 'creates a template with attributes' do
expect(chef_run).to create_template('/tmp/with_attributes').with(
user: 'user',
group: 'group',
backup: false,
)
expect(chef_run).to_not create_template('/tmp/with_attributes').with(
user: 'bacon',
group: 'fat',
backup: true,
)
end
it 'creates a template when specifying the identity attribute' do
expect(chef_run).to create_template('/tmp/identity_attribute')
end
end
Ресурс package
Рецепт
package 'explicit_action' do
action :remove
end
package 'with_attributes' do
version '1.0.0'
action :remove
end
package 'specifying the identity attribute' do
package_name 'identity_attribute'
action :remove
end
Тест единицы
require 'chefspec'
describe 'package::remove' do
let(:chef_run) { ChefSpec::SoloRunner.new(platform: 'ubuntu', version: '20.04').converge(described_recipe) }
it 'removes a package with an explicit action' do
expect(chef_run).to remove_package('explicit_action')
expect(chef_run).to_not remove_package('not_explicit_action')
end
it 'removes a package with attributes' do
expect(chef_run).to remove_package('with_attributes').with(version: '1.0.0')
expect(chef_run).to_not remove_package('with_attributes').with(version: '1.2.3')
end
it 'removes a package when specifying the identity attribute' do
expect(chef_run).to remove_package('identity_attribute')
end
end
Ресурс chef_gem
Рецепт
chef_gem 'default_action'
chef_gem 'explicit_action' do
action :install
end
chef_gem 'with_attributes' do
version '1.0.0'
end
chef_gem 'specifying the identity attribute' do
package_name 'identity_attribute'
end
Тест единицы
require 'chefspec'
describe 'chef_gem::install' do
let(:chef_run) { ChefSpec::SoloRunner.new(platform: 'ubuntu', version: '20.04').converge(described_recipe) }
it 'installs a chef_gem with the default action' do
expect(chef_run).to install_chef_gem('default_action')
expect(chef_run).to_not install_chef_gem('not_default_action')
end
it 'installs a chef_gem with an explicit action' do
expect(chef_run).to install_chef_gem('explicit_action')
end
it 'installs a chef_gem with attributes' do
expect(chef_run).to install_chef_gem('with_attributes').with(version: '1.0.0')
expect(chef_run).to_not install_chef_gem('with_attributes').with(version: '1.2.3')
end
it 'installs a chef_gem when specifying the identity attribute' do
expect(chef_run).to install_chef_gem('identity_attribute')
end
end
Ресурс directory
Рецепт
directory '/tmp/default_action'
directory '/tmp/explicit_action' do
action :create
end
directory '/tmp/with_attributes' do
user 'user'
group 'group'
end
directory 'specifying the identity attribute' do
path '/tmp/identity_attribute'
end
Тест единицы
require 'chefspec'
describe 'directory::create' do
let(:chef_run) { ChefSpec::SoloRunner.new(platform: 'ubuntu', version: '20.04').converge(described_recipe) }
it 'creates a directory with the default action' do
expect(chef_run).to create_directory('/tmp/default_action')
expect(chef_run).to_not create_directory('/tmp/not_default_action')
end
it 'creates a directory with an explicit action' do
expect(chef_run).to create_directory('/tmp/explicit_action')
end
it 'creates a directory with attributes' do
expect(chef_run).to create_directory('/tmp/with_attributes').with(
user: 'user',
group: 'group',
)
expect(chef_run).to_not create_directory('/tmp/with_attributes').with(
user: 'bacon',
group: 'fat',
)
end
it 'creates a directory when specifying the identity attribute' do
expect(chef_run).to create_directory('/tmp/identity_attribute')
end
end
Гвардейцы
Рецепт
service 'true_guard' do
action :start
only_if { 1 == 1 }
end
service 'false_guard' do
action :start
not_if { 1 == 1 }
end
service 'action_nothing_guard' do
action :nothing
end
Тест единицы
require 'chefspec'
describe 'guards::default' do
let(:chef_run) { ChefSpec::SoloRunner.new(platform: 'ubuntu', version: '20.04').converge(described_recipe) }
it 'includes resource that have guards that evaluate to true' do
expect(chef_run).to start_service('true_guard')
end
it 'excludes resources that have guards evaluated to false' do
expect(chef_run).to_not start_service('false_guard')
end
it 'excludes resource that have action :nothing' do
expect(chef_run).to_not start_service('action_nothing_guard')
end
end
Метод include_recipe
Рецепт
include_recipe 'include_recipe::other'
Тест единицы
require 'chefspec'
describe 'include_recipe::default' do
let(:chef_run) { ChefSpec::SoloRunner.new(platform: 'ubuntu', version: '20.04').converge(described_recipe) }
it 'includes the `other` recipe' do
expect(chef_run).to include_recipe('include_recipe::other')
end
it 'does not include the `not` recipe' do
expect(chef_run).to_not include_recipe('include_recipe::not')
end
end
Несколько действий
Рецепт
service 'resource' do
action :start
end
service 'resource' do
action :nothing
end
Тест единицы
require 'chefspec'
describe 'multiple_actions::sequential' do
let(:chef_run) { ChefSpec::SoloRunner.new(platform: 'ubuntu', version: '20.04', log_level: :fatal).converge(described_recipe) }
it 'executes both actions' do
expect(chef_run).to start_service('resource')
end
it 'does not match other actions' do
expect(chef_run).to_not disable_service('resource')
end
end
Для получения дополнительной информации …
Для получения дополнительной информации о ChefSpec:
© Chef Software, Inc.
Licensed under the Creative Commons Attribution 3.0 Unported License.
The Chef™ Mark and Chef Logo are either registered trademarks/service marks or trademarks/servicemarks of Chef, in the United States and other countries and are used with Chef Inc's permission.
We are not affiliated with, endorsed or sponsored by Chef Inc.
https://docs.chef.io/workstation/chefspec/