Имя: Пароль:
IT
Мобильный мир
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());
        }
    }
Проблемы невозможно решaть нa том же уровне компетентности, нa котором они возникaют. Альберт Эйнштейн