ChefSpec
Используйте ChefSpec для моделирования сходимости ресурсов на узле:
- Является расширением RSpec, фреймворка разработки на основе поведенческого подхода (BDD) для Ruby
- Самый быстрый способ тестирования ресурсов и рецептов
ChefSpec — это фреймворк, который тестирует ресурсы и рецепты как часть имитируемого выполнения Chef Infra Client. Тесты ChefSpec выполняются очень быстро. При использовании в процессе разработки кулинарных книг, тесты ChefSpec часто являются первым индикатором проблем, которые могут существовать в кулинарной книге.
Запуск ChefSpec
ChefSpec входит в состав Chef Workstation. Чтобы запустить ChefSpec:
chef exec rspec
Тесты модулей
RSpec — это фреймворк разработки на основе поведенческого подхода (BDD), который использует естественный язык предметно-ориентированного языка (DSL) для быстрого описания сценариев тестирования систем. RSpec позволяет настроить сценарий и затем выполнить его. Результаты сравниваются с набором определенных ожиданий.
ChefSpec построен на основе DSL RSpec.
Синтаксис
Синтаксис тестов, основанных на 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равно одному,sumодного плюс равно двум -
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 полезен для обработки платформо-специфичных сценариев. Например, «При работе на платформе A, проверьте 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 '/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: '16.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 '/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: '16.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 '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: '16.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: '16.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 '/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: '16.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: '16.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: '16.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: '16.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/