|
Android, автоматические тесты | ☑ | ||
---|---|---|---|---|
0
Волшебник
модератор
25.01.17
✎
21:11
|
Пишу программу для мобильных устройств типа смартфонов/планшетов на платформе Android (язык Java) и возник вопрос про автоматизированное тестирование.
Уже появился некий базовый функционал, который не должен испортиться при любом рефакторинге, и хочется иметь уверенность, что он не сломался и работает при любых изменениях. Какова стратегия написания тестов? То, что полное покрытие всех условий в программе невозможно, это понятно. Вы вообще тестируете свои программы автоматически? |
|||
1
Asmody
25.01.17
✎
21:16
|
||||
2
Волшебник
модератор
25.01.17
✎
21:28
|
(1) Читал. Я вообще на этом сайте жил некоторое время. В итоге понял, что он сам не понимает, что пишет.
|
|||
3
Asmody
25.01.17
✎
21:31
|
(2) Ну, механику написания тестов в AS он описывает нормально.
|
|||
4
Asmody
25.01.17
✎
21:32
|
(2) Надо тебе книжку по Андроиду написать :)
|
|||
5
Волшебник
модератор
25.01.17
✎
21:35
|
(3) Вот смотри. У меня есть база данных со справочниками, документами и регистрами типа 1С. Мне нужно тестами удостовериться, что справочники хранят данные, документы записываются и проводятся, регистры выдают правильные итоги.
Что за тесты мне нужны? Подозреваю: 1. Создание фиктивных элементов справочников. 2. Создание фиктивных документов по ним и их проведение. 3. Проверка итогов по регистрам. 4. Удаление фиктивных объектов. Правильно? |
|||
6
Волшебник
модератор
25.01.17
✎
21:36
|
(4) Пока рано. Ещё сам не понимаю. :)
|
|||
7
Asmody
25.01.17
✎
22:01
|
(5) Как-то так. Это интеграционное и функциональное тестирование.
|
|||
8
Волшебник
модератор
27.01.17
✎
22:10
|
Создал первые тесты. Протестировал простую функцию formatAmount методом полного покрытия всех условий. Да, теперь я действительно уверен в правильности работы этой функции. Она действительно работает. Вопросов к ней больше нет.
Подскажите стратегию написания тестов. Нельзя же каждое условие в программе протестировать... Или можно? То есть будем каждую функцию в программе доводить до состояния доказанности? И нельзя же запускать тестирование ночью и утром молиться, что ничего не сломалось... Или так и делают? Мы покрываем тестами все условия типа "белого ящика", или тестируем общую функциональность в режиме "чёрного ящика". И ещё вопрос, как тестировать формы (активити), когда пользователь может тыкнуть в любое место? |
|||
9
eks1985
27.01.17
✎
22:41
|
(8) Так тестирование это целая отдельная планета =)
Не скажу по Android, скажу про js, хоть и в js еще пешком под стол хожу, но все же. Во-первых определиться: сначала тесты, потом код или сначала код потом тесты. Первый вариант эффективнее, т.к. не будет кода не покрытого тестом, т.к. пишется сначала тест + в процессе написания теста лучше понимаешь что же хочешь закодить. Но этот подход требует самодисциплины, обычно хочется поскорей что-нибудь закодить, а тут эти тесты...Короче TDD подход (test driven development) Во-вторых тесты конечно бывают разные: юнит тесты, интеграционные тысты, тесты интерфейса. Я бы вообще по началу задумался только о юнит тестах, т.к. пишутся наименее затратно в формате гипотеза - вызов метода - проверка: гипотеза подтвердилась? Самые замороченные наверное интерфейсные данные, т.к. требуют обычно сложных моков - тестовых данных, да и протестировать все многообразие интерактивных действий очень сложно. В js обычно начинают с того что тупо тестят что по всем маршрутам все компоненты рендерятся и не падают. А по поводу когда тесты запускать, под js тут просто уйма тест раннеров, сохранил код, запустился тест раннер, прошли тесты, причем конечно де факто запускаются не все тесты, а только на код измененный с момента последнего коммита. Но на js то все это хорошо, т.к. почти всегда разработка при работающем дев сервере и сборка моментальная, особенно если hot module replacement используется. Как оно под нативный Андроид, хз, наверняка схожий подход. >>И ещё вопрос, как тестировать формы (активити), когда пользователь может тыкнуть в любое место? Обычно тестируются определенные users story: "Пользователь ткнул в поле ввода и тогда тра тата, затем пользователь чекнул чекбок и тогда тра тата". Ну и вообще есть такое понятие как cove coverage. Понятное дело что невозможно весь код покрыть тестами, но даже если он покрыт на 30% это лучше чем отсутствие тестов. Меня в тестировании больше всего напрягает, что при изменении функционала приходится и тесты тоже переписывать. |
|||
10
Волшебник
27.01.17
✎
23:26
|
(9) Прочитал. Много думал.
|
|||
11
eks1985
27.01.17
✎
23:52
|
Подумалось, а откуда это я столько умных слов знаю про тесты, причем сам то их почти еще не пишу =)
И вот вспомнилось https://www.youtube.com/watch?v=WpkDN78P884 Robert Martin Очень полезное видео всмысле вправки мозгов |
|||
12
pumbaEO
27.01.17
✎
23:54
|
Чик чик и в продакшен - вот судьба адьінєсников.
|
|||
13
eks1985
28.01.17
✎
00:01
|
(11) Конкретно с 58 минуты про TDD, но лучше полностью посмотреть.
|
|||
14
Torquader
28.01.17
✎
00:06
|
В интерфейсе тесты и поведение можно в код обработки интерфейса закладывать, чтобы было несколько путей работы, а всё остальное было заранее запрещено - тогда и писать проще, и как бы, тестирование идёт сразу с написанием.
P.S. если программа разбита на отдельные объекты, у которых явно определены все методы, то протестировать можно каждый объект - причём достаточно просто. |
|||
15
oleg_prg
28.01.17
✎
02:18
|
2 года пишу на java не резу на покрывал код тестами.
|
|||
16
oleg_prg
28.01.17
✎
02:23
|
Все тестирование делаю в основном руками, пишу сценарии работы и проверяю на реальной программе. Конечно делаю автоматические наполнения базы записями, моделирую нагрузки и прочее, до продакшена сам с товарищами, знакомыми юзаю приложение, но тесты в AS не использую.
|
|||
17
oleg_prg
28.01.17
✎
02:26
|
Больше уделяю вопросам работоспособности app на различных устройствах, под разные формфакторы и платформы, как правило в этом больше всего косяков.
|
|||
18
eks1985
28.01.17
✎
08:39
|
(16) Тут автор больше обеспокоен регрессионным тестированием, и вполне обоснованно. Автотесты позволяют править код не бояюсь что-то сломать. Вручную же не будешь тестировать всю систему после каждого изменения.
|
|||
19
Волшебник
модератор
28.01.17
✎
10:12
|
Хорошее дело я затеял. Пока писал тесты, нашёл несколько не очевидных ошибок.
|
|||
20
oleg_prg
28.01.17
✎
11:16
|
(19) обязательно займусь авто-тестами, стало интересно )))
|
|||
21
Asmody
28.01.17
✎
11:46
|
TDD меняет мышление. Оно становится более инженерным. Теряется момент творчества. Кроме того, часто придумать тесты сложнее, чем решить задачу.
Ну и конечно ключевой вопрос: кто будет тестировать тесты? |
|||
22
Волшебник
модератор
28.01.17
✎
13:51
|
(21) Если тесты надо тестировать, то их надо удалить.
|
|||
23
Asmody
28.01.17
✎
14:25
|
(22) Ну, как-бы подразумевается априори, что тесты пишутся без ошибок. Так может лучше сразу писать программы без ошибок?
|
|||
24
Волшебник
модератор
28.01.17
✎
15:13
|
(23) Было бы идеально, но это невозможно по причине их сложности.
Если же функция простая, то не нужно её покрывать тестами аки бык муравья, так что в тестах тоже нужна разумность (благоразумность). Вот я и спрашивал, есть ли какая-то конкретная стратегия между двумя крайностями - чёрный и белый ящик. Я знаю методику тестирования граничных условий, но она подходит для работы с числами. Например, проверяются числа из каждого диапазона плюс особенные значения, например, 0, -1, +9999999, 0.000015. |
|||
25
Волшебник
модератор
28.01.17
✎
15:30
|
Не могу из теста передать в программу нормальный контекст.
Пока выкрутился так public class TestGolden extends MockContext{ Но этот MockContext не поддерживает всякие getSharedPreferences и ещё SQLiteOpenHelper требует нормальный контекст. Чё делать? |
|||
26
Asmody
28.01.17
✎
15:54
|
(25) Функции там мокать можно?
|
|||
27
Волшебник
модератор
28.01.17
✎
16:16
|
Внутри тест-класса вызов getApplicationContext() выбрасывает UnsupportedOperationException
откуда тест-класс может получить нормальный контекст типа Activity? fucking java... |
|||
29
Волшебник
модератор
28.01.17
✎
16:41
|
временное решение ниже, но ActivityInstrumentationTestCase2 зачёркнуто как depricated. Вместо него Гугл советует ActivityTestRule, но с ним не работает или я что-то не понял...
public class TestGolden extends ActivityInstrumentationTestCase2<Desktop> { private Desktop desktop; public TestGolden() { super(Desktop.class); } @Before public void setUp() throws Exception { super.setUp(); desktop = getActivity(); } desktop - нормальная активити, полноценный потомок Context |
|||
30
Волшебник
модератор
28.01.17
✎
17:01
|
заработало с ActivityTestRule
надо так: public class TestGolden2 extends ActivityTestRule<Desktop> { private Desktop desktop; @Rule public ActivityTestRule<Desktop> desktopTestRule = new ActivityTestRule(Desktop.class); public TestGolden2() { super(Desktop.class); desktop = desktopTestRule.getActivity(); } |
|||
31
Волшебник
модератор
28.01.17
✎
17:16
|
Поправка.
public class GoldenTest extends ActivityTestRule<Desktop> { @Rule public ActivityTestRule<Desktop> desktopTestRule = new ActivityTestRule<>(Desktop.class); public GoldenTest() { super(Desktop.class); } @Test public void test_Application_formatAmount() { //public class Application { // public String formatAmount(BigDecimal amount, boolean abbreviateMillions) { Application app = new Application(desktopTestRule.getActivity()); ____ fucking Java, fucking Android... |
|||
32
Волшебник
модератор
28.01.17
✎
17:21
|
(31)+ Ах, да. Тесты теперь фиксируются так:
Assert.assertEquals(expected, actual); |
|||
33
Волшебник
модератор
28.01.17
✎
17:58
|
Оно работает!
![]() |
|||
34
eks1985
28.01.17
✎
18:37
|
Набор тестов вручную запускаешь? Или автоматом при билде проекта?
|
|||
35
Волшебник
модератор
28.01.17
✎
19:05
|
(34) Правой кнопкой на классе и Run.
Можно каждый тест стартовать отдельно. |
|||
36
Волшебник
модератор
28.01.17
✎
19:07
|
Пример мощного интеграционного теста:
Программно создаём элементы справочников, документ, проводим его и проверяем, что остатки по регистру увеличились на заданную сумму. @Test public void test_currencyTotals() throws Exception{ Application app = new Application(desktopTestRule.getActivity()); try { Currency currency = app.configuration.currencies.findOrCreateItem("тугрики"); Group group = app.configuration.groups.findOrCreateItem("семья"); Account account = app.configuration.accounts.findOrCreateItem("кошелёк"); account.group = group; account.save(); BigDecimal currencyAmountBefore = app.configuration.regAccounts.getCurrencyAmount(account,currency); BigDecimal amountDelta = app.newAmount(100.01); Operation doc = app.configuration.operations.getNew(); doc.type = Operation.OPERATION_TYPE.INCOME; doc.accountDR = account; doc.currencyDR = currency; doc.amountDR = amountDelta; doc.save(); //and post BigDecimal currencyAmountAfter = app.configuration.regAccounts.getCurrencyAmount(account,currency); Assert.assertEquals(amountDelta, currencyAmountAfter.subtract(currencyAmountBefore)); } catch (Exception e) { throw new Exception(e.getMessage()); } } |
Форум | Правила | Описание | Объявления | Секции | Поиск | Книга знаний | Вики-миста |