- PVSM.RU - https://www.pvsm.ru -

Автоматизированное тестирование плагинов Redmine

За прошедший год мы разработали четыре плагина для связки Redmine [1] + Telegram [2] (раз [3], два [4], три [5], четыре [6]).

Потихонечку вырабатываются свои Best Practices в отношении них. В этой заметке расскажем о тестировании и интеграции с Travis CI [7].

Автоматизированное тестирование плагинов Redmine - 1


Почему решили о тестировании рассказать? Потому что тестирование плагина для Redmine [1] — тот ещё квест.

Вначале...

Привычный rspec [8] с первого захода не получилось прикрутить. Со второго и третьего тоже.

Примеров плагинов с хорошим покрытием тестами также не удалось найти. Большинство плагинов либо совсем без тестов, либо сами плагины такие простые, что тесты для них помещались в одном файле. Как правило это были тесты для вьюх. А нам нужно тестировать логику.

Лучшим примером оказались тесты самого Redmine. В этом проекте используются обычные rails default тесты без какого-либо маломальского оформления (пруф [9]).

Отсутствие контекстов внутри одного теста затрудняет тестировать различные ситуации для одного и того же класса. Поэтому прямое следование такому подходу завело нас однажды в тупик.

Очень выручила книга Rails Test Prescription [10] издания 2011 года. Из неё мы узнали про так как писать Rails default style тесты, а таже про гем shoulda [11], который и стал нашим спасением.

Примеры будем приводить из плагина redmine_2fa [3], который мы анонсировали [12] в прошлом месяце. А также из новоиспечённого redmine_telegram_common [6], о котором тоже однажды расскажем.

Как запускать тесты?

Тесты запускаются и корня Redmine:

bundle exec rake redmine:plugins:test NAME=redmine_2fa

На версии Redmine 3.3.1 при выполнении этой команды сыпется много ruby warning-ов, чтобы сделать выдачу почище, нужно добавить следующую строку в test_helper.rb:

$VERBOSE = nil # for hide ruby warnings

Для использования своих фикстур в тестах, в тот же файл нужно добавить

ActiveRecord::FixtureSet.create_fixtures(File.dirname(__FILE__) + '/fixtures/', [:auth_sources])

Где вместо :auth_sources будут те фикстуры, которые необходимо подгрузить к тестам.

Gemfile

group :test do
  gem 'shoulda'
end

shoulda [11] — must have gem для тестирования плагинов. Остальные — в зависимости от потребностей для тестирования.

Тестирование патчей для контроллеров

class AccountControllerPatchTest < ActionController::TestCase
  # В начале теста указываем какой контроллер тестируем
  tests AccountController

  # Загружаем фикстуры
  fixtures :users, :email_addresses, :roles, :auth_sources

  # Инициализируем общий для всех тестов контекст
  setup do
    @user = User.find(2) # jsmith
    Redmine2FA.stubs(:active_protocols).returns(Redmine2FA::AVAILABLE_PROTOCOLS)
  end

  # Переходим к частным контекстам
  context 'user without 2fa' do
    context 'with valid login data' do
    setup { post :login, username: 'jsmith', password: 'jsmith', back_url: 'http://test.host/' }

    context 'prepare' do
      should set_session[:otp_user_id].to(2)
      should set_session[:otp_back_url].to('http://test.host/')
      should 'set user instance variable' do
        assert_equal @user, assigns(:user)
      end
    end
  end
end

Stub-ы и mock-и

Нового искать не стали, используем гем mocha [13] который используется в Redmine.

Примеры использования:

User.any_instance.stubs(:mobile_phone).returns('7894561230')
User.any_instance.expects(:authenticate_otp).returns(true)

Тестирование маршрутов

Тесты для маршрутов располагаются в папке integration/routing.

В Redmine есть свои хелперы для тестирования:

should_route 'POST /redmine_2fa/bot/init' => 'otp_bot#create'

Но для сложных маршрутов, нужно использовать хелпер из рельс:

assert_routing({ method: 'post', path: '/redmine_2fa/bot/token:token/update' },
               controller: 'otp_bot_webhook', action: 'update', token: 'token:token')

Unit-тесты

Их нет смысла подробно описывать обойдёмся небольшим примером:

context 'confirmation' do
  setup do
    User.any_instance.stubs(:mobile_phone).returns('79243216547')
  end

  should 'confirm mobile phone with valid code' do
    User.any_instance.expects(:authenticate_otp).returns(true)
    @user.confirm_mobile_phone('valid')
    @user.reload
    assert @user.mobile_phone_confirmed?
  end

  should 'return errors with invalid code' do
    User.any_instance.expects(:authenticate_otp).returns(false)
    @user.confirm_mobile_phone('invalid')
    @user.reload
    assert !@user.mobile_phone_confirmed?
    assert @user.errors.present?
  end
end

Теперь про CI

Как вы помните из начала статьи, тесты плагинов запускаются из корня Redmine. А код плагина самого Redmine не содержит.

Поэтому в процесс тестирования нужно включить установку Redmine, инициализацию его базы и уже потом запуск тестов.

Пример настройки

.travis.yml

language: ruby
rvm:
  - 2.3.0

addons:
  postgresql: "9.4"

env:
  - REDMINE_VER=3.3-stable
  - REDMINE_VER=master

install: "echo skip bundle install"

before_script:
  - psql -c 'create database travis_ci_test;' -U postgres

script:
  - export TESTSPACE=`pwd`/testspace
  - export NAME_OF_PLUGIN=redmine_telegram_common
  - export PATH_TO_PLUGIN=`pwd`
  - export PATH_TO_REDMINE=$TESTSPACE/redmine
  - mkdir $TESTSPACE
  - cp test/support/* $TESTSPACE/
  - bash -x ./travis.sh

В install стоит

install: "echo skip bundle install"

потому что основная логика установки запускается в скрипте
bash -x ./travis.sh

travis.sh

#/bin/bash

set -e

if [[ ! "$TESTSPACE" = /* ]] ||
   [[ ! "$PATH_TO_REDMINE" = /* ]] ||
   [[ ! "$REDMINE_VER" = * ]] ||
   [[ ! "$NAME_OF_PLUGIN" = * ]] ||
   [[ ! "$PATH_TO_PLUGIN" = /* ]];
then
  echo "You should set"
       " TESTSPACE, PATH_TO_REDMINE, REDMINE_VER"
       " NAME_OF_PLUGIN, PATH_TO_PLUGIN"
       " environment variables"
  echo "You set:"
       "$TESTSPACE"
       "$PATH_TO_REDMINE"
       "$REDMINE_VER"
       "$NAME_OF_PLUGIN"
       "$PATH_TO_PLUGIN"
  exit 1;
fi

export RAILS_ENV=test

export REDMINE_GIT_REPO=git://github.com/redmine/redmine.git
export REDMINE_GIT_TAG=$REDMINE_VER
export BUNDLE_GEMFILE=$PATH_TO_REDMINE/Gemfile

# checkout redmine
git clone $REDMINE_GIT_REPO $PATH_TO_REDMINE
cd $PATH_TO_REDMINE
if [ ! "$REDMINE_GIT_TAG" = "master" ];
then
  git checkout -b $REDMINE_GIT_TAG origin/$REDMINE_GIT_TAG
fi

mv $TESTSPACE/database.yml.travis config/database.yml
mv $TESTSPACE/additional_environment.rb config/

# create a link to the backlogs plugin
ln -sf $PATH_TO_PLUGIN plugins/$NAME_OF_PLUGIN

# install gems
bundle install

# run redmine database migrations
bundle exec rake db:migrate

# run plugin database migrations
bundle exec rake redmine:plugins:migrate

bundle exec rake db:structure:dump

# run tests
bundle exec rake redmine:plugins:test NAME=$NAME_OF_PLUGIN

Если будете пользоваться этим скриптом, то конфиг тестовой базы нужно разместить в файле test/support/database.yml.travis

database.yml.travis

test:
  adapter: postgresql
  encoding: unicode
  pool: 5
  database: travis_ci_test
  user: postgres

Резюме

Коротенько по основным пунктам прошлись. О деталях и частностях — продолжим в комментариях. Больше примеров для тестов — в исходниках redmine_2fa [3] и redmine_telegram_common [6].

Автор: Centos-admin.ru

Источник [14]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/ruby/208688

Ссылки в тексте:

[1] Redmine: http://www.redmine.org/

[2] Telegram: https://telegram.org/

[3] раз: https://github.com/centosadmin/redmine_2fa

[4] два: https://github.com/centosadmin/redmine_intouch

[5] три: https://github.com/centosadmin/redmine_chat_telegram

[6] четыре: https://github.com/centosadmin/redmine_telegram_common

[7] Travis CI: https://travis-ci.org/

[8] rspec: http://rspec.info/

[9] пруф: https://github.com/redmine/redmine/blob/master/test/unit/issue_test.rb

[10] Rails Test Prescription: https://pragprog.com/book/nrtest/rails-test-prescriptions

[11] shoulda: https://github.com/thoughtbot/shoulda

[12] анонсировали: https://habrahabr.ru/company/centosadmin/blog/312656/

[13] mocha: https://github.com/freerange/mocha

[14] Источник: https://habrahabr.ru/post/314954/