Файл spec/spec_helper.cr, созданный командой crystal init, используется в качестве точки входа в тесты проекта. Этот файл обычно требует спецификации, исходного кода проекта, а также любых других файлов, специфичных для спецификации, таких как фикстуры или макеты. Здесь также могут быть определены глобальные помощники тестирования. Каждый тест должен требовать, чтобы этот файл имел доступ к модулю Spec и другим помощникам.
В предыдущем примере мы использовали только утверждение eq, то есть два значения равны. Однако модуль Spec предоставляет множество других утверждений, как показано в следующем примере:
require "spec"
it do
true.should be_true
nil.should be_nil
10.should be >= 5
"foo bar baz".should contain "bar"
10.should_not eq 5
expect_raises Exception, "Err" do
raise Exception.new "Err"
end
end
Полный список см. на https://crystal-lang.org/api/Spec/Expectations.html. Этот пример также демонстрирует, что внешний блок #describe не требуется. Однако обычно рекомендуется включить один из них, поскольку он помогает в организации тестов. Однако блок #it необходим, поскольку без него сообщения об ошибках не будут корректно сообщаться.
По мере роста количества кода в приложении будет расти и количество тестов. Это может затруднить отладку конкретных тестовых случаев. В этом случае аргумент focus: true можно добавить в блок #describe или #it. При этом будет выполнена только одна спецификация, как в следующем примере:
it "does something", focus: true do
1.should eq 1
end
Только не забудьте удалить его перед совершением!
Модуль Spec также предоставляет некоторые дополнительные методы, которые можно использовать для более точного контроля выполнения ваших тестовых случаев. Некоторые из них перечислены здесь:
• #pending: этот метод используется для определения тестового примера для чего-то, что еще не полностью реализовано, но будет реализовано в будущем, например, ожидающий "check cat" { cat.alive? }. Блок метода никогда не выполняется, но может использоваться для описания того, что должен делать тест.
• #pending!: Метод #pending! аналогичен предыдущему методу, но может использоваться для динамического пропуска тестового примера. Это может быть полезно для обеспечения выполнения зависимостей/требований системного уровня перед запуском тестового примера.
• #fail: Наконец, этот метод можно использовать для ручного провала тестового примера. Это можно использовать в сочетании с пользовательской условной логикой для создания более сложных утверждений, с которыми не могут справиться встроенные утверждения.
Маркировка (Tagging) тестов
Теги — это способ организовать спецификации в группы, чтобы можно было выполнить их подмножество. Подобно фокусировке спецификации, теги применяются к блокам #describe или #it через аргумент tags следующим образом:
require "spec"
describe "tags" do
it "tag a", tags: "a" do
end
it "tag b", tags: "b" do
end
end
Отсюда вы можете использовать опцию --tag через crystal spec, чтобы контролировать, какие из них будут выполняться, как описано здесь:
• --tag 'a' --tag 'b' будет включать спецификации, отмеченные ИЛИ b.
• --tag '~a' --tag '~b' будет включать спецификации, не помеченные знаком И, не помеченные знаком b.
• --tag 'a' --tag '~b' будет включать спецификации, отмеченные тегом a, но не отмеченные тегом b.
Последняя команда может выглядеть так: crystal spec --tag 'a'. Далее мы рассмотрим, как обрабатывать зависимости внутренних объектов путем создания макетов.
Осмеяние (Mocking)
Предыдущий пример с методом #add не имел никаких внешних зависимостей, но помните в Главе 4 «Изучение Crystal посредством написания интерфейса командной строки», как мы сделали NotificationEmitter типом аргумента конструктора, а не использовали его непосредственно в методе #process? Тип NotificationEmitter является зависимостью типа Processor.
Причина, по которой мы сделали его аргументом конструктора, заключается в том, что он следует нашим принципам проектирования SOLID (где SOLID означает принцип единой ответственности, принцип открытости-закрытости, принцип замены Лискова, принцип сегрегации интерфейса и принцип инверсии зависимостей), что, в свою очередь, делает тип легче для тестирования, позволяя использовать фиктивную реализацию вместо этого аргумента. Макет позволяет вам подтвердить, что он вызывается правильно, и настроить его на возврат значений так, чтобы тестовые примеры каждый раз были одинаковыми.