Как покрыть Activity тестами?

1,00
р.
Настаиваю библиотеку Jacoco. Пишу код в Android Studio 3.0. Есть MainActivity:
package houzz.com.hellojacoco
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private TextView text
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main)
findViewById(R.id.button).setOnClickListener(this) findViewById(R.id.hide).setOnClickListener(this) text = findViewById(R.id.text) }
@Override public void onClick(View v) { if (v.getId() == R.id.button) { text.setText("Hello World!") } else { v.setVisibility(View.GONE) } } }
Для экрана есть тест MainActivityTest:
@RunWith(AndroidJUnit4.class) @LargeTest public class MainActivityTest {
@Rule public final ActivityTestRule rule = new ActivityTestRule<>(MainActivity.class)
@Test public void test1() { onView(withId(R.id.hide)).perform(click()) onView(withId(R.id.hide)).check(matches(not(isDisplayed()))) }
@Test public void test2() { onView(withId(R.id.button)).perform(click()) onView(withId(R.id.text)).check(matches(withText("Hello World!"))) } }
В gradle подключил создание отчетов о покрытии кода, согласно ответу:
android { buildTypes { debug { testCoverageEnabled = true } } }
Далее запустил задачу проверки:
./gradlew createDebugCoverageReport
В отчете по пути hellojacoco\app\build
eports\coverage\debug\index.html получаю результат в 0% покртытия тестами. Как покрыть код Activity на 100%? Может по другому настроить? Стандартный отчет из Android Studio, так же показывает 0%
P.S. Пробовал делать по разным статьям, например по этой. Ни один результат не учитывает тесты из папкиAndroidTest. Максимум JUnit тесты учитываются. Так же в этой статье есть ссылка на GitHub, а так же ветка с обновлением для Android Stduio 3.0, но у меня все равно отчеты не получаются корректно.
P.S.S. Оказывается основная проблемма была в телефоне, отчет с телефона Samsung получить не получается, получил через эмулятор. Но работатет все равно не до конца корректно. Вот как сейчас выглядит покрытие Activity:

То есть в отчет включается только test2, а должен и test1. Если удалить test2, test1 учитывается. Если тесты объединить в один, покрытие будет 100%, но если их будет много, тогда будет не удобно получать отчеты, о том какие тесты выполнились а какие нет.
Ссылка на весь проект
Ответ ниже помогает в некоторых моментах лучше понять вопрос, но не дает хорошего решения проблемы.

Ответ
Я когда-то пробовал по вот этому настраивать.
И из текста вашего вопроса не совсем понятно какую разметку и код вы используете (я про вторую кнопку - последний пример - непонятно что должно произойти по нажатию) Это помогло бы понять что не так с тестом.
Но хочу отметить 2 вещи (может вам поможет):
Тесты если не указан порядок исполняются в любом порядке (иначе нужно аннотировать класс теста так например @FixMethodOrder(MethodSorters.NAME_ASCENDING)) Если вы считаете что нажатие не отрабатывает по тому что провалиается тест - попробуйте заменить .check(matches(not(isDisplayed()))) на .check(doesNotExist())
Но для получения развернутого ответа - выложите пожалуйста полные исходники.
UPDATED (Добавлено после проверки всех исходников)
Все отлично у вас работает, но есть моменты:
Какого поведения среды исполнения вы ждете при изменении видимости? v.setVisibility(View.GONE) будет выполнено в потоке UI ПОСЛЕ завершения работы onClick(). Ожидать чего то другого - невозможно. Поэетому jacoco в отчете всегда будет указывать пропущенную инструкцию - тк проверить что она была выполнена она не может физически. В условиях всегда будут пропущенные бранчи - тк оно считатет ВСЕ возможные логические состояния и состояния аргумента. Те null, невозможность вызвать метод итд. Попробуйте сделать обработчики анонимными объектами - я так проверял: findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { text.setText("Hello World!") }}) findViewById(R.id.hide).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { v.setVisibility(View.GONE) }})

В этом случае не будет пропущенных бранчей. Будет одна пропущенная инструкция (уставка видимости). И покрытие - 93%. Я думаю это лучший из возможных результатов.
А вообще - я бы очень не советовал гоняться за 100% покрытием, особенно если надо "переписывать код под тесты". Ну и надо понимать - что не все корректно оно может учесть, просто в меру особенностей среды исполнения. И помнить что оно анализирует ФИНАЛЬНЫЙ скомпилированный код в байткоде. Например если вы switch-case сделаете - будет на каждый выбор по 2 бранча + если это числа то + все пропущенные - те полная хэш-таблица всех состояний. (правда там както обещали фильтр сделать такого - не проверял)
UPDATED (Дополнительные пояснения по комментариям)
Я вижу вы все упираетесь - попробую пояснить :) Объедините все в один тест вот так:
@Test public void test1() { onView(withId(R.id.hide)).perform(click()) onView(withId(R.id.hide)).check(matches(not(isDisplayed())))
onView(withId(R.id.button)).perform(click()) onView(withId(R.id.text)).check(matches(withText("Hello World!"))) }
И вы получите 100% покрытие и все будет нормально.
Почему? Потому что как я уже и писал:
Тесты выполняются не по порядку. У вас по обоим нажатиям срабатывает onClick() и только после его завершения меняется текст или прячется кнопка в UI потоке. Те в памяти получается при 2х раздельных тестах - 2 раздельных асинхронных вызова - и в одном из них одно отработает, в другом другое.
Те в каком порядке тесты выполняются - мы не знаем, в каком порядке асинхронные вызовы и операции изменения текста и видимости произойдут - мы не знаем. Знаем только одно - это изолированные друг от друга вызовы с созданием асинхронной задачи и последующей синхронизацией с UI потоком - а в каком порядке оно будет - "как карта ляжет". Поэтому и такой результат - мы не знаем какую из 2х асинхронных операций оно анализирует.
Какие есть счетчики в jacoco почитайте тут. ОБЯЗАТЕЛЬНО прочтите про анализ покрытия кода тут.
И думаю потом вы поймете - что при расстановке проб jacoco - ставит их по вашему коду 'как есть'. А в реальности - там 2 асинхронных вызова + они инициируются 2мя асинхронными тестами. И + это все еще оптимизирует компилятор. Те какая 'каша' из проб и асинхронностей в результате - никто не знает. Поэтому асинхронные вызовы тестируются только в рамках ОДНОГО теста на покрытие - тк это гарантирет очередность операций.
Можно еще упралять выполнением тестов espresso с помощью idling resources Но здесь мы имеем дело с работой анализатора jacoco - в тестах же нет проблем.