Файл: Одерски Мартин, Спун Лекс, Веннерс Билл, Соммерс ФрэнкО41 Scala. Профессиональное программирование. 5е изд спб. Питер, 2022. 608 с. ил. Серия Библиотека программиста.pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 09.12.2023
Просмотров: 739
Скачиваний: 11
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
25 .4 . Использование тестов в качестве спецификаций 583
must
, а не should
, то можно примешать
MustMatchers
. Например, примеши
вание
MustMatchers позволит вам создавать следующие выражения:
result must be >= 0
map must contain key 'c'
Если последнее утверждение не подтвердится, то будет показано сообщение об ошибке следующего вида:
Map('a' –> 1, 'b' –> 2) did not contain key 'c'
Среда тестирования specs2 — средство с открытым кодом, написанное на
Scala Эриком Торреборре (Eric Torreborre), — также поддерживает BDD
стиль тестирования, но с другим синтаксисом. Например, specs2 можно ис
пользовать для создания теста, показанного в листинге 25.5.
Листинг 25.5. Спецификация и тестирование поведения с использованием среды specs2
import org.specs2.*
import Element.elem object ElementSpecification extends Specification:
"A UniformElement" should {
"have a width equal to the passed value" in {
val ele = elem('x', 2, 3)
ele.width must be_==(2)
}
"have a height equal to the passed value" in {
val ele = elem('x', 2, 3)
ele.height must be_==(3)
}
"throw an IAE if passed a negative width" in {
elem('x', -2, 3) must throwA[IllegalArgumentException]
}
}
В specs2, как и в ScalaTest, существует DSLязык выявления соответствий.
Некоторые примеры работы средств выявления соответствий, имеющихся в specs2, показаны в листинге 25.5 в строках, содержащих must be_==
и must throwA
1
. Среду specs2 можно использовать в автономном режиме, но она также интегрируется со ScalaTest и JUnit, поэтому specs2тесты можно за
пускать и с этими инструментами.
1
Программное средство specs2 можно загрузить с сайта specs2.org.
584 Глава 25 • Утверждения и тесты
Одной из основных идей BDD является то, что тесты могут помогать обмени
ваться мнениями людям, принимающим решения о характере поведения про
граммных средств, людям, разрабатывающим программные средства, и людям, определяющим степень завершенности и работоспособности программных средств. В этом ключе могут применяться любые стили, имеющиеся в ScalaTest или specs2, однако в ScalaTest есть специально разработанный для этого стиль
AnyFeatureSpec
. Пример его использования показан в листинге 25.6.
Листинг 25.6. Использование тестов для содействия обмену мнениями среди всех заинтересованных сторон import org.scalatest.*
import org.scalatest.featurespec.AnyFeatureSpec class TVSetSpec extends AnyFeatureSpec, GivenWhenThen:
Feature("TV power button") {
Scenario("User presses power button when TV is off") {
Given("a TV set that is switched off")
When("the power button is pressed")
Then("the TV should switch on")
pending
}
}
Стиль
AnyFeatureSpec разработан для того, чтобы направить в нужное русло обсуждений предназначение программных средств: вам следует выявить специ фические требования, а затем дать им точное определение в виде
скриптов. Сосредоточиться на переговорах об особенностях отдельно взятых скриптов помогают методы
Given
,
When и
Then
, предоставляемые трейтом
GivenWhenThen
. Вызов pending в самом конце показывает: и тест, и само по
ведение не реализованы, имеется лишь спецификация. Как только будут реализованы все тесты и конкретные действия, тесты будут пройдены и тре
бования можно будет посчитать выполненными.
25 .5 . Тестирование на основе свойств
Еще одним полезным средством тестирования для Scala является Sca
laCheck — среда с открытым кодом, созданная Рикардом Нильсоном (Rickard
Nilsson). Она позволяет указывать свойства, которыми должен обладать тестируемый код. Для каждого свойства ScalaCheck создает данные и вы
дает утверждения, проверяющие наличие тех или иных свойств. Пример использования ScalaCheck из ScalaTest
AnyWordSpec
, в который примешан трейт
ScalaCheckPropertyChecks
, показан в листинге 25.7.
25 .5 . Тестирование на основе свойств
1 ... 56 57 58 59 60 61 62 63 64
585
Листинг 25.7. Тестирование на основе свойств с помощью ScalaCheck import org.scalatest.wordspec.AnyWordSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks import org.scalatest.matchers.must.Matchers.*
import Element.elem class ElementSpec extends AnyWordSpec,
ScalaCheckPropertyChecks:
"elem result" must {
"have passed width" in {
forAll { (w: Int) =>
whenever (w > 0) {
elem('x', w % 100, 3).width must equal (w % 100)
}
}
}
}
AnyWordSpec
— класс стиля, имеющийся в ScalaTest. Трейт
Sca laCheckPro- pertyChecks предоставляет несколько методов forAll
, позволяющих смеши
вать тесты на основе проверки наличия свойств с традиционными тестами на основе утверждений или выявления соответствий. В данном примере проверяется наличие свойства, которым должен обладать фабричный метод elem
. Свойства ScalaCheck выражены в виде значений функций, получающих в качестве параметров данные, необходимые для утверждений о наличии свойств. Эти данные будут генерироваться ScalaCheck. В свойстве, показан
ном в листинге 25.7, данными является целое число w
, которое представляет ширину. Внутри тела функции показан следующий код:
whenever (w > 0) {
elem('x', w % 100, 3).width must equal (w % 100)
}
Директива whenever указывает на то, что при каждом вычислении левосто
роннего выражения в true правостороннее также должно быть true
. Таким образом, в данном случае выражение в блоке должно быть true всякий раз, когда w
больше нуля. А правостороннее будет выдавать true
, если ширина, переданная фабричному методу elem
, будет равна ширине объекта
Element
, возвращенного фабричным методом.
При таком небольшом объеме кода ScalaCheck сгенерирует несколько пробных значений, проверяя каждое из них в поисках значения, для ко
торого свойство не соблюдается. Если свойство соблюдается для каж
дого значения, испытанного с помощью ScalaCheck, то тест будет прой
ден. В противном случае он будет завершен с генерацией исключения
586 Глава 25 • Утверждения и тесты
TestFailedException
, которое содержит информацию, включающую зна
чение, вызвавшее сбой.
25 .6 . Подготовка и проведение тестов
Во всех средах, упомянутых в текущей главе, имеется механизм для под
готовки и проведения тестов. В данном разделе будет дан краткий обзор того подхода, который используется в ScalaTest. Чтобы получить подробное описание любой из этих сред, нужно обратиться к их документации.
Подготовка больших наборов тестов в ScalaTest проводится путем вложения
Suite
наборов в
Suite
наборы. При выполнении
Suite
набор запустит не только свои тесты, но и все тесты вложенных в него
Suite
наборов. Вложен
ные
Suite
наборы, в свою очередь, выполнят тесты вложенных в них
Suite
наборов и т. д. Таким образом, большой набор тестов будет представлен деревом
Suite
объектов. При выполнении в этом дереве корневого
Suite
объекта будут выполнены все имеющиеся в нем
Suite
объекты.
Наборы можно вкладывать вручную или автоматически. Чтобы вложить их вручную, вам нужно либо переопределить метод nestedSuites в своих
Suite
классах, либо передать предназначенные для вложения
Suite
объекты в кон
структор класса
Suites
, который для этих целей предоставляет ScalaTest.
Для автоматического вложения имена пакетов передаются имеющемуся в ScalaTest средству Runner, которое определит
Suite
объекты автоматиче
ски, вложит их ниже корневого
Suite и выполнит корневой
Suite
Имеющееся в ScalaTest приложение Runner можно вызвать из командной строки или через такие средства сборки, как sbt или maven
. Самый распро
страненный способ запуска ScalaTest, скорее всего, с помощью sbt
1
. Напри
мер, для запуска тестового класса, показанного в листинге 25.6, с помощью sbt создайте новый каталог, поместите тестовый класс в файл с именем
TVSetSpec.scala в подкаталог src/test/scala и добавьте следующий файл build.sbt в новый каталог:
name := "ThankYouReader!"
scalaVersion := "3.0.0"
libraryDependencies += "org.scalatest" %% "scalatest" %
"3.2.9" % "test"
1
Установить sbt вы можете с сайта https://www.scalasbt.org/.
Резюме 587
Затем вы можете войти в оболочку sbt
, набрав sbt
:
$ sbt
[info] welcome to sbt 1.5.2 (AdoptOpenJDK Java 1.8.0_262)
sbt:ThankYouReader!>
Вам будет выдан запрос на ввод названия проекта, в данном случае
ThankYouReader!
Если вы введете test
, он скомпилирует и запустит тестовый класс. Результат показан на рис. 25.1.
Рис. 25.1. Вывод, полученный при запуске org .scalatest .run
Резюме
В этой главе мы показали примеры примешивания утверждений непосред
ственно в рабочий код, а также способы их внешней записи в тестах. Вы увидели, что программисты, работающие на Scala, могут воспользоваться преимуществами таких популярных средств тестирования от сообщества
Java, как JUnit, и более новыми средствами, разработанными исключи
тельно для Scala, например ScalaTest, ScalaCheck и specs2. И утверждения, встроенные в код, и внешние тесты могут помочь повысить качество ваших программных продуктов.
Глоссарий
Алгебраический тип данных (algebraic data type). Тип, определяемый путем предоставления нескольких альтернатив, у каждой из которых есть собственный конструктор. Обычно предоставляется возможность его декомпозиции через сопоставление с образцом. Эта концепция встре
чается в языках специ фикаций и функциональных языках програм
мирования. В Scala алгебраические типы данных можно эмулировать с помощью case
классов.
Альтернатива (alternative). Ветвь выражения match
. Имеет вид «
case
пат-
терн => выражение». Альтернативу также могут называть вариантом
(case).
Аннотация (annotation). Встречается в исходном коде и закрепляется за какойто частью синтаксиса. Аннотации обрабатываются компью
тером, поэтому их фактически можно использовать для расширения
Scala.
Анонимная функция (anonymous function). Альтернативное название функ
ционального литерала.
Анонимный класс (anonymous class). Синтетический подкласс, сгенериро
ванный компилятором Scala из выражения new
, в котором вслед за именем класса или трейта идут фигурные скобки. В них содержится тело анонимного подкласса, которое может быть пустым. Но если имя, указанное после выражения new
, ссылается на трейт или класс с абстрактными членами, то эти члены необходимо конкретизировать внутри фигурных скобок, определяющих тело анонимного подкласса.
Аргумент (argument). При вызове функции каждому ее параметру соответ
ствует передаваемый аргумент. Параметр — это переменная, которая ссылается на аргумент. Аргумент — это объект, который передается во время вызова. Кроме того, приложения могут принимать аргументы
(командной строки), содержащиеся в массиве
Array[String]
, который передается методам main объектоводиночек.
Глоссарий 589
Блок (block). Одно или несколько выражений или объявлений, заключен
ных в фигурные скобки. При вычислении блока все его выражения и объявления обрабатываются по очереди, после чего блок возвращает в качестве результата значение последнего выражения. Блоки обычно используются в качестве тел функции, выражений for
, циклов while и в любых других местах, где нужно сгруппировать какоето коли
чество инструкций. Если говорить более формально, то блок — это конструкция для инкапсуляции, в которой видны только побочные эффекты и итоговое значение. Таким образом, фигурные скобки, в ко
торых определяется класс или объект, не формируют блок, поскольку поля и методы (определенные внутри этих скобок) видны снаружи.
Такие фигурные скобки составляют шаблон.
Вариантность (variance). Параметр типа для класса или трейта можно поме
тить с помощью аннотации вариантности: либо как ковариантный (
+
), либо как контравариантный (
-
). Такие аннотации определяют, как у обобщенного класса или трейта работает отношение наследования.
Например, обобщенный класс
List ковариантен в своем параметре типа, поэтому
List[String]
является подтипом
List[Any]
. По умол
чанию, если не указывать аннотации
+
или
-
, то параметры типов
нонвариантны.
Виртуальная машина Java (Java Virtual Machine, JVM). Среда выполнения, в которой работают программы на языке Scala.
Возврат (return). В программах на языке Scala функция возвращает зна
чение. Вы можете называть его результатом функции. Кроме того, можно сказать, что функция приводит к значению. Результат любой функции в Scala — объект.
Вспомогательная функция (helper function). Предназначена для предостав
ления какихлибо возможностей для одной или нескольких других функций поблизости. Вспомогательные функции часто реализуются как локальные.
Вспомогательный конструктор (auxiliary constructor). Внутри фигурных скобок с определением класса можно указывать дополнительные конструкторы, которые выглядят как определения методов с именем this
, но без результирующего типа.
Вспомогательный метод (helper method). Вспомогательная функция, явля
ющаяся членом класса. Вспомогательные методы зачастую являются приватными.
590 Глоссарий
Выдача (yield). Выражение может выдавать (yield) результат. Ключевое слово yield обозначает результат выражения for
Вызов (invoke). Вы можете вызвать метод, функцию или замыкание с ар
гументами, то есть при выполнении их тела будут использованы за
данные аргументы.
Выражение (expression). Любой фрагмент кода в Scala дает какойлибо ре
зультат. Можно сказать, что результат вычисляется из выражения или выражение возвращает значение.
Выражение генератора (generator expression). Генерирует последователь
ность значений в выражении for
. Например, в for(i
<-
1
to
10)
вы
ражением генератора выступает
1
to
10
Выражение фильтра (filter expression). Булево выражение, которое идет за инструкцией if в выражении for
. В for(i
<-
1
to
10;
if i
%
2
==
0)
фильтрующим выражением выступает i
%
2
==
0
Генератор (generator). Определяет именованное значение и последовательно присваивает ему результаты выражения for
. Например, в for(i
<-
1
to
10)
генератором выступает i
<-
1
to
10
. Значение справа от
<-
— это
выражение генератора.
Замыкание (closure). Функциональный объект, который захватывает свобод
ные переменные и как будто замыкается вокруг переменных, видимых в момент его создания.
Значение (value). Результат любого вычисления или выражения в Scala.
При этом все значения в Scala являются объектами. Значение, в сущ
ности, — это образ объекта в памяти (в куче JVM или стеке).
Императивный стиль (imperative style). В этом стиле программирования акцент делается на том, в какой последовательности выполняются операции, чтобы их побочные эффекты проявились в правильном по
рядке. Этот стиль характеризуется итерацией с циклами, изменением данных без копирования и методами с побочными эффектами. Эта парадигма доминирует в таких языках, как C, C++, C# и Java, контра
стируя с функциональным стилем.
Инвариант (invariant). Термин имеет два значения. Это может быть свой
ство, которое всегда остается истинным при условии, что структура данных имеет правильный формат. Например, инвариантом отсор
тированного двоичного дерева может быть требование, согласно ко
торому каждый узел должен быть упорядочен перед своим правым подузлом, если таковой имеется. Термин «инвариант» также иногда