Spec-Zone.ru › Chef 16

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 построен на основе 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:

  • Репозиторий 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