Добро пожаловать! Если вы пропустили первую часть нашего путешествия , то, возможно, вы захотите сначала вернуться и наверстать упущенное.
До сих пор мы применяли процесс разработки через тестирование для создания нашего приложения наряду с использованием популярной среды тестирования RSpec. Отсюда мы собираемся исследовать некоторые другие функции RSpec, а также изучить использование драгоценного камня Pry для отладки и написания кода.
Другие особенности RSpec
Давайте уделим немного времени рассмотрению некоторых других функций RSpec, которые нам не нужны в этом простом примере приложения, но вам может пригодиться работа над собственным проектом.
Заявление в ожидании
Представьте, что вы пишете тест, но вас прерывают, или вам нужно уйти на собрание и еще не выполнили код, необходимый для прохождения теста.
Вы можете удалить тест и переписать его позже, когда сможете вернуться к своей работе. Или же вы можете просто закомментировать код, но это довольно уродливо и определенно бесполезно при использовании системы контроля версий.
Лучшее, что можно сделать в этой ситуации, — определить наш тест как «ожидающий», поэтому при каждом запуске тестовая среда будет игнорировать тест. Для этого вам нужно использовать ключевое слово pending
:
1
2
3
4
5
|
describe «some method» do
it «should do something»
pending
end
end
|
Установка и отрыв
Все хорошие тестовые среды позволяют выполнять код до и после каждого теста. RSpec ничем не отличается.
Он предоставляет нам методы « before
и « after
которые позволяют нам настроить определенное состояние для запуска нашего теста, а затем очистить это состояние после выполнения теста (это так, чтобы состояние не утекло и не повлияло на результат последующие испытания).
01
02
03
04
05
06
07
08
09
10
11
12
13
|
describe «some method» do
before(:each) do
# some set-up code
end
after(:each) do
# some tear-down code
end
it «should do something»
pending
end
end
|
Блоки контекста
Мы уже видели блок описаний; но есть другой блок, который функционально эквивалентен, называется context
. Вы можете использовать его везде, где вы будете использовать, describe
.
Разница между ними тонкая, но важная: context
позволяет нам определить состояние для нашего теста. Хотя это и не явно (мы на самом деле не устанавливаем состояние, определяя блок context
— оно вместо этого предназначено для удобства чтения, поэтому цель следующего кода более понятна).
Вот пример:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
describe «Some method» do
context «block provided» do
it «yields to block» do
pending
end
end
context «no block provided» do
it «calls a fallback method» do
pending
end
end
end
|
Столбики
Мы можем использовать метод- stub
чтобы создать поддельную версию существующего объекта и вернуть ему заранее определенное значение.
Это полезно для предотвращения касания нашими тестами API-интерфейсов живых сервисов и управления нашими тестами, давая предсказуемые результаты от определенных вызовов.
Представьте, что у нас есть класс Person
и у этого класса есть метод talk. Мы хотим проверить, работает ли этот метод так, как мы ожидаем. Чтобы сделать это, мы заглушим метод speak
используя следующий код:
01
02
03
04
05
06
07
08
09
10
|
describe Person do
it «speak()» do
bob = stub()
bob.stub(:speak).and_return(‘hello’)
Person.any_instance.stub(:initialize).and_return(bob)
instance = Person.new
expect(instance.speak).to eq(‘hello’)
end
end
|
В этом примере мы говорим, что «любой экземпляр» класса Person
должен иметь свой метод initialize
чтобы он возвращал объект bob
.
Вы заметите, что bob
сам по себе является заглушкой, которая настроена так, что любой временный код пытается выполнить метод talk, он возвращает «привет».
Затем мы приступаем к созданию нового экземпляра Person
и передаем вызов instance.speak
в синтаксис expect
RSpec.
Мы сообщаем RSpec, что ожидаем, что вызов вызовет строку «привет».
Последовательные возвращаемые значения
В предыдущих примерах мы использовали функцию RSpec and_return
чтобы указать, что наша заглушка должна возвращать при вызове.
Мы можем указывать разные возвращаемые значения каждый раз, когда вызывается заглушка, указав несколько аргументов для метода and_return
:
1
2
3
4
5
6
|
obj = stub()
obj.stub(:foo).and_return(1, 2, 3)
expect(obj.foo()).to eq(1)
expect(obj.foo()).to eq(2)
expect(obj.foo()).to eq(3)
|
Mocks
Моды похожи на заглушки в том смысле, что мы создаем поддельные версии наших объектов, но вместо того, чтобы возвращать предопределенное значение, мы более точно определяем маршруты, которые должны пройти наши объекты, чтобы тест был действительным.
Для этого мы используем метод mock
:
01
02
03
04
05
06
07
08
09
10
11
|
describe Obj do
it «testing()» do
bob = mock()
bob.should_receive(:testing).with(‘content’)
Obj.any_instance.stub(:initialize).and_return(bob)
instance = Obj.new
instance.testing(‘some value’)
end
end
|
В приведенном выше примере мы создаем новый экземпляр Object
и затем вызываем его метод testing
.
За кулисами этого кода мы ожидаем, что метод testing
будет вызван со значением 'content'
. Если он не вызывается с этим значением (а в приведенном выше примере это не так), то мы знаем, что какой-то фрагмент нашего кода не функционировал должным образом.
Тематический блок
Ключевое слово subject
можно использовать несколькими способами. Все из которых предназначены для уменьшения дублирования кода.
Вы можете использовать его неявно (обратите внимание, что наш блок it
вообще не ссылается на subject
):
1
2
3
4
5
6
|
describe Array do
describe «with 3 items» do
subject { [1,2,3] }
it { should_not be_empty }
end
end
|
Вы можете использовать его явно (обратите внимание, что наш блок it
относится непосредственно к subject
):
1
2
3
4
5
6
7
8
9
|
describe MyClass do
describe «initialization» do
subject { MyClass }
it «creates a new instance» do
instance = subject.new
expect(instance).to be_a(MyClass)
end
end
end
|
Вместо того, чтобы постоянно ссылаться на тему в вашем коде и передавать разные значения для создания экземпляров, например:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
describe «Foo» do
context «A» do
it «Bar» do
baz = Baz.new(‘a’)
expect(baz.type).to eq(‘a’)
end
end
context «B» do
it «Bar» do
baz = Baz.new(‘b’)
expect(baz.type).to eq(‘b’)
end
end
context «C» do
it «Bar» do
baz = Baz.new(‘c’)
expect(baz.type).to eq(‘c’)
end
end
end
|
Вместо этого вы можете использовать subject
вместе с let
чтобы уменьшить дублирование:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
describe «Person» do
subject { Person.new(name) } # Person has a get_name method
context «Bob» do
let(:name) { ‘Bob’ }
its(:get_name) { should == ‘Bob’ }
end
context «Joe» do
let(:name) { ‘Joe’ }
its(:get_name) { should == ‘Joe’ }
end
context «Smith» do
let(:name) { ‘Smith’ }
its(:get_name) { should == ‘Smith’ }
end
end
|
Для RSpec доступно гораздо больше функций, но мы рассмотрели наиболее важные из них, которые вы часто будете использовать при написании тестов с использованием RSpec.
Рандомизированные тесты
Вы можете настроить RSpec для запуска ваших тестов в случайном порядке. Это позволяет вам гарантировать, что ни один из ваших тестов не будет зависеть от других тестов вокруг него.
1
2
3
|
RSpec.configure do |config|
config.order = ‘random’
end
|
Вы можете установить это с помощью команды, используя параметр --order
flag /. Например: rspec --order random
.
Когда вы используете --order random
опцию —order, RSpec отобразит случайное число, которое использовалось для заполнения алгоритма. Вы можете снова использовать это «начальное» значение, если считаете, что обнаружили проблемы с зависимостями в своих тестах. Как только вы исправите то, что, по вашему мнению, является проблемой, вы можете передать начальное значение в RSpec (например, если начальное число было 1234
затем выполнить --order random:1234
), и оно будет использовать то же самое рандомизированное начальное число, чтобы посмотреть, может ли оно реплицировать оригинальная ошибка зависимости.
Глобальная конфигурация
Вы видели, что мы добавили специфический для проекта набор объектов конфигурации в наш Rakefile
. Но вы можете установить параметры конфигурации глобально, добавив их в файл .rspec
в вашем домашнем каталоге.
Например, внутри .rspec
:
1
|
—color —format nested
|
Отладка с Прай
Теперь мы готовы начать изучать, как мы можем отлаживать наше приложение и наш тестовый код, используя гем Pry.
Важно понимать, что хотя Pry действительно хорош для отладки вашего кода, на самом деле он предназначен как улучшенный инструмент Ruby REPL (для замены irb
), а не только в целях отладки; так, например, нет встроенных функций, таких как: шаг в, шаг или шаг и т. д., которые вы обычно найдете в инструменте, предназначенном для отладки.
Но как инструмент отладки, Pry очень сфокусирован и прост.
Мы вернемся к отладке через минуту, но давайте сначала рассмотрим, как мы будем первоначально использовать Pry.
Обновленный пример кода
В целях демонстрации Pry я собираюсь добавить больше кода в мой пример приложения (этот дополнительный код никак не влияет на наш тест)
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
class RSpecGreeter
attr_accessor :test
@@class_property = «I’m a class property»
def greet
binding.pry
@instance_property = «I’m an instance property»
pubs
privs
«Hello RSpec!»
end
def pubs
test_var = «I’m a test variable»
test_var
end
private
def privs
puts «I’m private»
end
end
|
Вы заметите, что мы добавили некоторые дополнительные методы, свойства экземпляра и класса. Мы также делаем вызовы двух новых методов, которые мы добавили из нашего метода greet
.
Наконец, вы заметите использование binding.pry
.
Установка точек binding.pry
с binding.pry
Точка останова — это место в вашем коде, где выполнение остановится.
Вы можете установить несколько точек останова в своем коде и создавать их с помощью binding.pry
.
Когда вы запустите свой код, вы заметите, что терминал остановится и поместит вас в код вашего приложения в том месте, где был установлен ваш binding.pry.
Ниже приведен пример того, как это может выглядеть …
1
2
3
4
5
6
|
8: def greet
=> 9: binding.pry
10: pubs
11: privs
12: «Hello RSpec!»
13: end
|
С этого момента у Pry есть доступ к локальной области видимости, так что вы можете использовать Pry так же, как и в irb
и начать вводить, например, переменные, чтобы увидеть, какие значения они содержат.
Вы можете запустить команду exit
чтобы выйти из Pry и продолжить выполнение кода.
Найти, где вы находитесь: whereami
При использовании большого количества точек binding.pry
может быть трудно понять, где в приложении вы находитесь.
Чтобы лучше понять, где вы находитесь в любой момент, вы можете использовать команду whereami
.
При самостоятельном запуске вы увидите нечто похожее на то, как вы использовали binding.pry
(вы увидите строку, на которой была установлена точка останова, и пару строк выше и ниже этого). Разница в том, что если вы передадите дополнительный числовой аргумент, например, whereami 5
вы увидите пять дополнительных строк выше, где был размещен whereami 5
. Например, вы можете запросить 100 строк вокруг текущей точки останова.
Эта команда может помочь вам ориентироваться в текущем файле.
Трассировка стека: wtf
Команда wtf
означает «что такое f ***» и предоставляет полную трассировку стека для самого последнего возникшего исключения. Это может помочь вам понять шаги, приводящие к появившейся ошибке.
Проверять: ls
Команда ls
показывает, какие методы и свойства доступны для Pry.
При запуске он покажет вам что-то вроде …
1
2
3
|
RSpecGreeter#methods: greet pubs test test=
class variables: @@class_property
locals: _ __ _dir_ _ex_ _file_ _in_ _out_ _pry_
|
В приведенном выше примере мы видим, что у нас есть четыре открытых метода (помните, что мы обновили наш код, attr_accessor
в него некоторые дополнительные методы, а затем были созданы test
и test=
при использовании сокращения Ruby attr_accessor
).
Он также отображает другие классовые и локальные переменные, к которым Pry может получить доступ.
Еще одна полезная вещь, которую вы можете сделать, это выполнить поиск (поиск) только по тому, что вас интересует. Вам нужно иметь представление о регулярных выражениях, но это может быть удобным методом. Вот пример …
1
2
|
ls -p -G ^p
=> RSpecGreeter#methods: privs
|
В приведенном выше примере мы используем параметры / flags -p
и -G
, которые сообщают Pry, что мы хотим видеть только открытые и закрытые методы, и используем регулярное выражение ^p
(что означает совпадение с любым, начинающимся с p
) в качестве шаблона поиска для фильтровать результаты.
Запуск ls --help
также покажет вам все доступные опции.
Изменение области действия: cd
Вы можете изменить текущую область с помощью команды cd
.
В нашем примере, если мы запустим cd ../pubs
это cd ../pubs
нас к результату вызова этого метода.
Если мы сейчас запустим whereami
вы увидите, что Inside "I'm a test variable"
будет отображаться Inside "I'm a test variable"
.
Если мы запустим self
то увидим, что мы получили "I'm a test variable"
.
Если мы запустим self.class
мы увидим возвращенную String
.
Вы можете перемещаться вверх по цепочке областей действия с помощью cd ..
или вы можете вернуться на верхний уровень области действия с помощью cd /
.
Примечание: мы могли бы добавить еще один binding.pry
внутри метода pubs
и тогда наша область действия была бы внутри этого метода, а не в результате метода.
Видя, как глубоко вы: nesting
Рассмотрим предыдущий пример работы cd pubs
. Если мы запустим команду nesting
мы увидим верхний уровень количества контекстов / уровней, которые в данный момент есть у Pry:
1
2
3
4
|
Nesting status:
—
0. # (Pry top level)
1. «I’m a test variable»
|
Оттуда мы можем запустить exit
чтобы вернуться к более раннему контексту (например, внутри метода greet
)
Повторный запуск exit
означает, что мы закрываем последний контекст, который есть у Pry, и поэтому Pry завершает работу, и наш код продолжает выполняться.
Найдите любой метод: find-method
Если вы не уверены, где найти конкретный метод, вы можете использовать команду find-method
чтобы показать вам все файлы в вашей базе кода, у которых есть метод, который соответствует тому, что вы ищете:
01
02
03
04
05
06
07
08
09
10
11
|
find-method priv
=> Kernel
Kernel#private_methods
Module
Module#private_instance_methods
Module#private_constant
Module#private_method_defined?
Module#private_class_method
Module#private
RSpecGreeter
RSpecGreeter#privs
|
Вы также можете использовать опцию / -c
для поиска содержимого файлов:
1
2
3
4
|
find-method -c greet
=> RSpecGreeter
RSpecGreeter: def greet
RSpecGreeter#privs: greet
|
Классическая отладка: next
, step
, continue
Хотя описанные выше методы полезны, на самом деле это не «отладка» в том смысле, в котором вы, вероятно, привыкли.
Для большинства разработчиков их редактор или браузер предоставляют им встроенный инструмент отладки, который позволяет им фактически шаг за шагом проходить через код и следовать по маршруту, по которому проходит код, до его завершения.
Поскольку Pry разработан для использования в качестве REPL, это не значит, что он бесполезен для отладки.
Наивным решением было бы установить несколько операторов binding.pry
через метод и использовать ctrl-d для перемещения по каждому набору точек останова. Но это все еще не достаточно хорошо.
Для пошаговой отладки вы можете загрузить gem pry-nav …
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
source «https://rubygems.org»
gem ‘rspec’
group :development do
gem ‘guard’
gem ‘guard-rspec’
gem ‘pry’
# Adds debugging steps to Pry
# continue, step, next
gem ‘pry-remote’
gem ‘pry-nav’
end
|
Этот драгоценный камень расширяет Pry, поэтому он понимает следующие команды:
-
Next
(перейти к следующей строке) -
Step
(перейти к следующей строке и, если это метод, затем перейти к этому методу) -
Continue
(игнорировать дальнейшие точки останова в этом файле)
Непрерывная интеграция с Travis-CI
В качестве дополнительного бонуса давайте интегрируем наши тесты с онлайн-сервисом CI (непрерывная интеграция) Travis-CI .
Принцип CI заключается в том, чтобы фиксировать / выдвигать рано и часто, чтобы избежать конфликтов между вашим кодом и главной веткой. Когда вы это делаете (в данном случае мы подключаемся к GitHub), это должно запустить «сборку» на вашем CI-сервере, которая запускает соответствующие тесты, чтобы убедиться, что все работает так, как должно быть.
Теперь, когда TDD является основной методологией разработки, вы с меньшей вероятностью будете сталкиваться с ошибками при каждом нажатии, потому что ваши тесты являются неотъемлемой частью вашего рабочего процесса разработки, и поэтому, прежде чем вы начнете нажимать, вы уже будете знать об ошибках или регрессиях. Но это не обязательно защищает вас от ошибок, возникающих в интеграционных тестах (когда весь код в нескольких системах выполняется вместе, чтобы гарантировать, что система «в целом» функционирует правильно).
В любом случае, код никогда не должен передаваться напрямую на ваш рабочий сервер; его всегда следует сначала отправить на CI-сервер, чтобы помочь выявить возможные ошибки, возникающие в результате различий между средой разработки и производственной средой.
У многих компаний все еще есть больше сред, в которых их код должен пройти, прежде чем он достигнет рабочего сервера.
Например, на BBC News у нас есть:
- CI
- Тестовое задание
- стадия
- Прямой эфир
Хотя каждая среда должна быть идентична в настройке, цель состоит в том, чтобы реализовать различные типы тестирования, чтобы гарантировать, что многие ошибки будут обнаружены и устранены до того, как код достигнет «живого» состояния.
Трэвис-CI
Travis CI — это служба непрерывной интеграции для сообщества открытого исходного кода. Он интегрирован с GitHub и предлагает первоклассную поддержку нескольких языков
Это означает, что Travis-CI предлагает бесплатные услуги CI для проектов с открытым исходным кодом, а также имеет платную модель для предприятий и организаций, которые хотят сохранить частную интеграцию CI.
Мы будем использовать бесплатную модель с открытым исходным кодом в нашем примере GitHub.
Процесс такой:
- Зарегистрировать аккаунт на GitHub
- Войдите в Travis-CI, используя свою учетную запись GitHub
- Перейти на страницу « Аккаунты »
- Включите все репозитории, на которых вы хотите запустить CI
- Создайте файл
.travis.yml
в корневом каталоге вашего проекта и зафиксируйте его в своем репозитории GitHub.
Последний шаг является наиболее важным (создание файла .travis.yml
), поскольку он определяет параметры конфигурации для Travis-CI, поэтому он знает, как справиться с выполнением тестов для вашего проекта.
Давайте посмотрим на файл .travis.yml
который мы используем для нашего примера репозитория GitHub:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
language: ruby
cache: bundler
rvm:
— 2.0.0
— 1.9.3
script: ‘bundle exec rake spec’
bundler_args: —without development
branches:
only:
— master
notifications:
email:
|
Давайте разберем это по частям …
Сначала мы указываем, какой язык мы используем в нашем проекте. В этом случае мы используем Ruby: language: ruby
.
Поскольку запуск Bundler может быть немного медленным, и мы знаем, что наши зависимости не будут меняться, мы можем выбрать кэширование зависимостей, поэтому мы устанавливаем cache: bundler
.
Travis-CI использует RVM (Ruby Version Manager) для установки Ruby на своих серверах. Поэтому нам нужно указать, с какими версиями Ruby мы хотим запускать наши тесты. В данном случае мы выбрали 2.0
и 1.9.3
которые являются двумя популярными версиями Ruby (технически наше приложение использует Ruby 2, но хорошо знать, что наш код передается и в других версиях Ruby):
1
2
3
|
rvm:
— 2.0.0
— 1.9.3
|
Для запуска наших тестов мы знаем, что можем использовать команду rake
или rake spec
. Travis-CI по умолчанию запускает команду rake
но из-за того, как Gems устанавливаются на Travis-CI с помощью Bundler, нам нужно изменить команду по умолчанию: script: 'bundle exec rake spec'
. Если бы мы этого не сделали, у Travis-CI возникла бы проблема с поиском rspec/core/rake_task
который указан в нашем Rakefile
.
Примечание: если у вас есть какие-либо проблемы, связанные с Travis-CI, вы можете присоединиться к каналу #travis на IRC-экране, чтобы получить помощь в ответе на любые ваши вопросы. Вот где я обнаружил решение моей проблемы с Travis-CI, которая не могла выполнять мои тесты с помощью команды rake
умолчанию, и предложение перезаписать значение по умолчанию с помощью bundle exec rake
решило эту проблему.
Далее, поскольку мы заинтересованы только в запуске наших тестов, мы можем передать дополнительные аргументы Travis-CI, чтобы отфильтровать гемы, которые мы не хотим беспокоить при установке. Поэтому для нас мы хотим исключить установку гемов, сгруппированных как разработка: bundler_args: --without development
(это означает, что мы исключаем гемы, которые действительно используются только для разработки и отладки, такие как Pry и Guard).
Важно отметить, что изначально я загружал spec_helper.rb
в нашем файле spec_helper.rb
. Это вызвало проблему при запуске кода на Travis-CI, теперь я исключал гемы «разработки». Поэтому мне пришлось настроить код следующим образом:
1
|
require ‘pry’ if ENV[‘APP_ENV’] == ‘debug’
|
Вы можете видеть, что теперь гем APP_ENV
require
только в том случае, если для переменной среды APP_ENV
установлено значение debug. Таким образом, мы можем избежать появления ошибок в Travis-CI. Это означает, что при локальном запуске кода вам нужно установить переменную окружения, если вы хотите отлаживать код с помощью Pry. Ниже показано, как это можно сделать в одну строку:
1
|
APP_ENV=debug && ruby lib/example.rb
|
Были внесены два других изменения, которые я внес в наш Gemfile
. Один из них состоял в том, чтобы прояснить, какие гемы требовались для тестирования, а какие — для разработки, а другой был явно необходим Travis-CI:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
source «https://rubygems.org»
group :test do
gem ‘rake’
gem ‘rspec’
end
group :development do
gem ‘guard’
gem ‘guard-rspec’
gem ‘pry’
# Adds debugging steps to Pry
# continue, step, next
gem ‘pry-remote’
gem ‘pry-nav’
end
|
Глядя на обновленный выше Gemfile
мы видим, что мы переместили гем RSpec в новую группу test
, так что теперь должно быть яснее, для чего предназначен каждый гем. Мы также добавили новый gem 'rake'
. Документация Travis-CI утверждает, что это должно быть указано явно.
Следующий раздел является необязательным, и он позволяет вам помещать в белый (или черный список) определенные ветки в вашем хранилище. Поэтому по умолчанию Travis-CI будет запускать тесты для всех ваших веток, если вы не укажете обратное. В этом примере мы говорим, что хотим, чтобы он работал только с нашей master
веткой:
1
2
3
|
branches:
only:
— master
|
Мы могли бы сказать, что он запускает каждую ветку «кроме» определенной ветки, например так:
1
2
3
|
branches:
except:
— some_branch_I_dont_want_run
|
Последний раздел сообщает Travis-CI, куда отправлять уведомления при сбое или успешной сборке:
1
2
3
|
Вы можете указать несколько адресов электронной почты, если хотите:
1
2
3
4
5
|
Вы можете быть более конкретным и указать, что вы хотите, чтобы произошло в случае неудачи или успеха (например, больше людей будет интересоваться только в случае неудачи тестов, вместо того, чтобы получать по электронной почте каждый раз, когда они проходят):
1
2
3
4
5
6
|
Приведенный выше пример показывает, что получатель никогда не получит электронное письмо, если тесты пройдут, но получит уведомление, если состояние ошибки изменится (значение по умолчанию для обоих always
означает, что вы всегда будете уведомлены независимо от результата статуса).
Примечание: когда вы явно указываете on_failure
или on_success
вам необходимо переместить адреса электронной почты внутри ключа recipients
.
Вывод
На этом мы заканчиваем наши две части, посвященные RSpec, TDD и Pry.
В первой части нам удалось написать наше приложение с использованием процесса TDD и инфраструктуры тестирования RSpec. Во второй половине мы также использовали Pry, чтобы показать, как нам легче отлаживать работающее приложение Ruby. Наконец, мы смогли настроить наши тесты для работы в составе сервера непрерывной интеграции с использованием популярного сервиса Travis-CI.
Надеюсь, это дало вам достаточно вкуса, поэтому вы заинтересованы в дальнейшем изучении каждого из этих методов.