Если вы в чем-то похожи на меня, вам нравится, когда есть одна кнопка, на которую можно нажать, что я хочу. Что касается запуска тестов в Android Studio, мне нравится, что его легко настроить для запуска всех моих тестов с помощью кнопки запуска. Однако, когда количество и сложность тестов со временем растет, действия за этой одной кнопкой могут занять намного больше времени, чем мне хотелось бы, особенно если это тестирование кода приложения, с которым я в настоящее время не работаю.

Зачем запускать все тесты все время, когда вы действительно хотите запускать те, которые имеют значение прямо сейчас? Или хотя бы те, которые быстро бегают. К счастью, инструменты тестирования Android позволяют разделить тесты на связанные группы для более быстрого тестирования. Вот ваши варианты.

Сгруппировать по «размеру» теста

Я заключил здесь размер в кавычки, потому что это не то, что вы изначально думали. Дело не столько в объеме кода в тесте, сколько в том, что он делает, что может замедлить его работу. Инструменты тестирования Android предоставляют вам три категории размеров: малый, средний и большой. Чтобы поместить тест в каждую категорию, используйте одну из предоставленных аннотаций (@SmallTest, @MediumTest, @LargeTest) либо для отдельного метода тестирования, либо для класса, который содержит методы тестирования (в этом случае он применяется ко всем содержащимся в нем методам тестирования. ). Вот пример:

@SmallTest
fun testSomethingSimple() {
    // Test code...
}

Вы можете видеть здесь, что отдельный метод тестирования testSomethingSimple помечен знаком @SmallTest. Судя только по названию, он кажется хорошим кандидатом для так называемого «небольшого теста», но что на самом деле означает эта категория размера? Оказывается, у них есть очень конкретные описания, и я предлагаю щелкнуть, чтобы прочитать документацию по API для каждого из них. Обобщить:

  • Небольшие тесты вообще не выполняют операций ввода-вывода и должны выполняться менее 200 мс.
  • Средние тесты могут использовать локальные файлы и базы данных, но не сети и должны длиться менее 1000 мс.
  • Большие тесты могут использовать все, включая локальные и удаленные данные, для тестирования полной интеграции приложения.

Если ваши тесты запускают ваше приложение и проходят через фактические варианты использования, обращаясь к данным из удаленных API-интерфейсов и баз данных, все они, вероятно, классифицируются как большие тесты. Чтобы классифицировать тест как малый или средний, вам, возможно, придется изучить стратегии подделки, имитации или заглушки кода, выполняющего ввод-вывод. Это может потребовать значительных усилий для настройки, если вы еще не используете какую-либо форму внедрения зависимостей. Но я скажу, что в идеале вы, вероятно, захотите, чтобы как можно больше ваших тестов были «маленькими», даже если это потребует дополнительных инженерных усилий. Создание заглушек, имитация и подделка удаленных данных во время тестирования может значительно ускорить обратную связь с вашими тестами во время разработки. Однако эффективная реализация этого - совершенно новая тема!

Итак, допустим, у вас есть тесты, которые теперь разделены на категории по размеру. Чтобы ускорить циклы разработки и тестирования, вы можете запускать только небольшие тесты, чтобы быстро получить обратную связь, что ваш новый код работает, и что он сразу ничего не сломал. Что вам нужно сделать, так это организовать передачу параметра размер инструментальному средству выполнения тестов Android с помощью -e size small. Вы можете добавить это в build.gradle под defaultConfig следующим образом:

android {
    defaultConfig {
        testInstrumentationRunnerArguments size: 'small'
    }
}

Но вы, вероятно, не захотите делать это постоянной частью сборки. Вместо этого вы можете передать эту директиву через командную строку Gradle следующим образом:

-Pandroid.testInstrumentationRunnerArguments.size=small

При этом будут выполняться только тесты, помеченные @SmallTest. Это нормально для группировки по горизонтали, по размеру, но как насчет вертикальной, по характеристикам продукта?

Группировка по тестам по функциям (набор тестов)

Допустим, вы работаете только над одной функцией в своем приложении и очень хорошо знаете набор кода, выполняемого с помощью этой функции. Вместо того, чтобы запускать все тесты для вашего приложения, вы можете захотеть запустить только те тесты, которые реализуют эту функцию. Один из способов сделать это - использовать набор тестов. Набор тестов позволяет вам определить группу тестовых классов, которые должны выполняться вместе по запросу, без необходимости запускать каждый из них по отдельности.

Все, что вам нужно сделать, это определить пустой класс и дать ему две аннотации: @RunWith и @SuiteClasses. Вот простой набор под названием TestSuite:

import org.junit.runner.RunWith
import org.junit.runners.Suite
import org.junit.runners.Suite.SuiteClasses
@RunWith(Suite::class)
@SuiteClasses(Activity01Test::class, Activity02Test::class)
class TestSuite

@RunWith сообщает исполнителю тестов, что это набор, а @SuiteClasses перечисляет все тестовые классы, составляющие этот набор. Когда бегун встречает этот класс, он запускает тесты во всех названных классах. Вам нужно будет организовать использование этого одного класса так же, как и любого другого отдельного класса, передав -e class package.of.TestSuite01 средству выполнения инструментария. Вы можете добавить это в build.gradle в defaultConfig, например, используя его полное имя класса:

android {
  defaultConfig {
    testInstrumentationRunnerArguments class: 'pkg.of.TestSuite'
  }
}

Или через командную строку Gradle вот так:

-Pandroid.testInstrumentationRunnerArguments.class=pkg.of.TestSuite

О тестовых наборах нужно знать одну вещь. Если вы добавите класс набора тестов в свою коллекцию тестов и выберете запуск всех тестов, ваш набор тестов будет запускаться в дополнение ко всем другим тестам. Представьте себе такой макет пакета:

У нас есть тестовые классы, определенные в пакете my.testapp, и набор, определенный в my.testapp.suite, который использует некоторые классы из my.testapp. Если вы решите запустить все тесты для этого проекта или даже все тесты в пакете my.testapp, пакет будет дублировать усилия своих именованных классов и снова излишне замедлит работу. Чтобы этого избежать, организуйте свой набор и тестовые классы в разные пакеты, которые не являются вложенными:

При этом мы можем выбрать запуск всех тестов в пакете my.testapp.classes отдельно от пакета, передав -e package my.testapp.classes через build.gradle:

testInstrumentationRunnerArguments package: 'my.testapp.classes'

Или в командной строке:

-Pandroid.testInstrumentationRunnerArguments.package=my.testapp.classes

Группировка по тестам по другой общности (аннотация)

Наборы тестов - хороший способ сгруппировать тесты по некоторым общим функциям, но для этого требуется, чтобы вы перечислили их все в одном классе. Это может быть не очень удобно, а управлять длинными списками может быть сложно. Вместо этого может быть проще пометить каждый тестовый класс или метод как часть группы для совместного выполнения. Для этого вы можете создать настраиваемую аннотацию и использовать ее для пометки классов и методов для выполнения. Например, вот простое объявление аннотации:

package my.testapp
annotation class FeatureTest

Затем вы можете применить его к тестовому классу или отдельному методу:

@RunWith(AndroidJUnit4::class)
@FeatureTest
class Feature01Test {
    // test methods here
}

После того, как у вас есть классы и методы, отмеченные таким образом, вы можете передать аргумент средству выполнения тестов, чтобы указать, что только эти отмеченные тесты должны выполняться, используя -e annotation my.testapp.FeatureTest. В build.gradle вы должны использовать это:

testInstrumentationRunnerArguments annotation:
    'my.testapp.FeatureTest'

А в командной строке вот так:

-Pandroid.testInstrumentationRunnerArguments.annotation=my.testapp.FeatureTest

Обратите внимание, что этот метод использования аннотаций не упоминается в формальной документации по командной строке программы запуска тестов, но вы можете увидеть его в документации API для AndroidJUnitRunner.

Резюме

Группирование тестов может эффективно ускорить ваши циклы разработки, давая вам возможность выполнять только те тесты, которые имеют значение для того, над чем вы сейчас работаете, или только те тесты, которые у вас есть время для выполнения. Это также может ускорить процесс тестирования в средах CI. Например, вы можете захотеть запускать только более быстрые тесты с каждой фиксацией и более медленные тесты в ночное время. Или, в функциональной ветке, вы можете ограничить тестирование CI только функцией, находящейся в текущей разработке.

Еще статьи о более быстром тестировании на Android