Spec-Zone.ru › Chef 17

ChefSpec

[править на GitHub]

Используйте 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:

  • Репозиторий ChefSpec на GitHub

© 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/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API