Як ви тестуєте програму Android під кількома діями?


80

Ми створюємо складний додаток для Android, що складається з безлічі екранів та робочих процесів, розподілених у багатьох видах діяльності. Наші робочі процеси схожі на те, що ви можете бачити на банкоматі банку, наприклад, є Activityвхід в систему при переході до головного меню, Activityяке може переходити до інших видів діяльності на основі вибору користувача.

Оскільки у нас так багато робочих процесів, нам потрібно створити автоматизовані тести, які охоплюють кілька видів діяльності, щоб ми могли перевіряти робочий процес від кінця до кінця. Наприклад, використовуючи приклад банкомату, ми хотіли б ввести дійсний PIN-код, перевірити, що направляє нас до головного меню, вибрати зняти готівку, перевірити, що ми знаходимося на екрані зняття готівки тощо, і т.д., і врешті-решт опинитися повернутися в головне меню або "вийти".

Ми грали з тестовими API, які постачаються з Android (наприклад ActivityInstrumentationTestCase2), а також з Positron , але, здається, жоден з них не здатний тестувати за межі одного Activity, і хоча ми можемо знайти в цих інструментах деяку корисність для деяких модульних тестів, вони виграли не відповідає нашим потребам у тестуванні сценаріїв, які охоплюють різні дії.

Ми відкриті для фреймворку xUnit, сценаріїв, реєстраторів / відтворень графічного інтерфейсу тощо і будемо вдячні за будь-яку пораду.


2
Починаючи з Android 4.1, тепер є нова система тестування від Android, яка дозволяє проводити тестування за всіма видами діяльності, а то й для всієї системи: developer.android.com/tools/testing/testing_ui.html
Крістофер Орр,

1
Роботіум також задовольнить цю потребу, і це лише в декількох рядках.
Дорі

Відповіді:


65

Мені трохи незручно відповідати на власне запитання про щедрості, але ось воно ...

Я шукав високо і низько на цьому, і не можу повірити, що ніде немає опублікованої відповіді. Я підійшов дуже близько. Зараз я точно можу запускати тести, які охоплюють діяльність, але, схоже, у моїй реалізації є деякі проблеми з термінами, коли тести не завжди проходять надійно. Це єдиний приклад, який я знаю про те, що успішно тестуються різні дії. Сподіваємось, моє вилучення та анонімізація не призвело до помилок. Це спрощений тест, де я ввожу ім’я користувача та пароль для входу в систему, а потім спостерігаю, як на іншому заході привітання відображається належне привітальне повідомлення:

package com.mycompany;

import android.app.*;
import android.content.*;
import android.test.*;
import android.test.suitebuilder.annotation.*;
import android.util.*;
import android.view.*;
import android.widget.*;

import static org.hamcrest.core.Is.*;
import static org.hamcrest.core.IsNull.*;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.junit.Assert.*;
import static com.mycompany.R.id.*;

public class LoginTests extends InstrumentationTestCase {

   @MediumTest
   public void testAValidUserCanLogIn() {

      Instrumentation instrumentation = getInstrumentation();

      // Register we are interested in the authentication activiry...
      Instrumentation.ActivityMonitor monitor = instrumentation.addMonitor(AuthenticateActivity.class.getName(), null, false);

      // Start the authentication activity as the first activity...
      Intent intent = new Intent(Intent.ACTION_MAIN);
      intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
      intent.setClassName(instrumentation.getTargetContext(), AuthenticateActivity.class.getName());
      instrumentation.startActivitySync(intent);

      // Wait for it to start...
      Activity currentActivity = getInstrumentation().waitForMonitorWithTimeout(monitor, 5);
      assertThat(currentActivity, is(notNullValue()));

      // Type into the username field...
      View currentView = currentActivity.findViewById(username_field);
      assertThat(currentView, is(notNullValue()));
      assertThat(currentView, instanceOf(EditText.class));
      TouchUtils.clickView(this, currentView);
      instrumentation.sendStringSync("MyUsername");

      // Type into the password field...
      currentView = currentActivity.findViewById(password_field);
      assertThat(currentView, is(notNullValue()));
      assertThat(currentView, instanceOf(EditText.class));
      TouchUtils.clickView(this, currentView);
      instrumentation.sendStringSync("MyPassword");

      // Register we are interested in the welcome activity...
      // this has to be done before we do something that will send us to that
      // activity...
      instrumentation.removeMonitor(monitor);
      monitor = instrumentation.addMonitor(WelcomeActivity.class.getName(), null, false);

      // Click the login button...
      currentView = currentActivity.findViewById(login_button;
      assertThat(currentView, is(notNullValue()));
      assertThat(currentView, instanceOf(Button.class));
      TouchUtils.clickView(this, currentView);

      // Wait for the welcome page to start...
      currentActivity = getInstrumentation().waitForMonitorWithTimeout(monitor, 5);
      assertThat(currentActivity, is(notNullValue()));

      // Make sure we are logged in...
      currentView = currentActivity.findViewById(welcome_message);
      assertThat(currentView, is(notNullValue()));
      assertThat(currentView, instanceOf(TextView.class));
      assertThat(((TextView)currentView).getText().toString(), is("Welcome, MyUsername!"));
   }
}

Цей код, очевидно, не дуже читабельний. Я насправді витягнув його у просту бібліотеку з англомовним API, тому я можу просто сказати такі речі:

type("myUsername").intoThe(username_field);
click(login_button);

Я протестував приблизно до чотирьох видів діяльності і переконуюсь, що підхід працює, хоча, як я вже сказав, іноді виникає проблема з часом, яку я не до кінця зрозумів. Я все ще зацікавлений почути будь-які інші способи тестування в різних видах діяльності.


3
Ви можете спробувати додати анотацію FlakyTest, щоб повторити тест автоматично, коли проблеми із синхронізацією спричиняють його збій. Насправді це не рішення, але в деяких ситуаціях життєздатне рішення.
Carl Manaster

Дякуємо, що написали це! Я шукав щось із функціональністю ActivityMonitors для мого тестування. Я просто не міг їх знайти.
Peter Ajtai

Наскільки я знаю, нічого з того, що ви зробили вище, не можна зробити, використовуючиActivityInstrumentationTestCase2
ericn

будь-яка ідея, за якої умови, 'getInstrumentation (). waitForIdleSync ();' потрапить у нескінченну петлю? В операційній платі процесора Android 4.4.2_r2 я стикаюся з цією проблемою під час виконання тесту CTS.
ArunJTS

Я думаю, що мій син @ pajato1 знайшов та виправив вашу проблему з термінами. Його виправлення вирішило мою проблему. Ось що він сказав: "Я щойно помітив у javadoc, що Instrumentation.startActivitySync () блокував, поки нове Діяльність не було готовим, а потім повертало його, тому, схоже, Монітор не був необхідним. Видалення його довело, що це було правильно. Мій Теорія полягає в тому, що Монітор спричиняв перезапуск Activity, створений startActivitySync (), в деяких випадках через перегонові умови. Я витрачав деякий час на читання вихідного коду Android, але нічого не вискакувало мені як причину перегонового стану. "
pajato0

22

Погляньте на Robotium,
"тестову структуру з відкритим кодом, створену для того, щоб зробити автоматичне тестування чорних скриньок додатків Android набагато швидшим і простішим, ніж те, що можливо завдяки нестандартним тестуванням приладів Android".

Домашня сторінка: http://www.robotium.org/
Джерело: http://github.com/jayway/robotium

Зверніть увагу, що проект «Роботіум» підтримується компанією, в якій я працюю


привіт, чи є для цього інструмент реєстратора? Я перевірив багато веб-сайтів і знайшов testdroid, який записує сценарії та запускає його. На жаль, це не безкоштовна програма, чи знаєте ви якусь безкоштовну програму, яка виконує процес запису?
thndrkiss

@thndrkiss: Я не знаю жодного такого інструменту. Якщо ви розмістите запитання на форумі Robotium, ви, швидше за все, отримаєте кращу відповідь.
Jonas Söderström

2
Роботій - рятувальник життя. Це зробить ваш тест надзвичайно простим для написання (ви в основному розмовляєте з ним простою англійською мовою: натисніть на це, натисніть кнопку назад тощо). Ви можете протестувати що завгодно, але вам не потрібно знати крихітні деталі. У нього є принаймні дві основні переваги: ​​ви можете тестувати програми, у яких у вас немає вихідного коду, і він покладається на інтерфейс, який робить його дуже надійним (ви змінюєте свої контролери / моделі набагато більше, ніж ваші уявлення ...)
tiktak

8

Ви завжди можете використовувати Робоцій. Він підтримує тестування чорних ящиків, як і Selenium, але для Android. Ви знайдете його на Robotium.org


1
Минулого разу, коли я перевіряв, що Роботіум не може бути використаний у всіх видах діяльності. Це виправлено зараз? stackoverflow.com/questions/3840034 / ...
user77115

3
Він завжди працював у різних видах діяльності, якщо вони належать до одного процесу.
Ренас

4

Я здивований, що ніхто не згадував про деякі провідні засоби автоматизованого функціонального тестування . Порівняно з Robotium, для цього не потрібно писати код Java.

MonkeyTalk : інструмент з відкритим кодом за підтримки компанії Gorilla Logic. Плюси: забезпечує запис, а також мову сценаріїв вищого рівня, простіше для нетехнічних користувачів, і є міжплатформенним (включаючи iOS). Враховуючи ці переваги як вимоги, ми виявили, що це найкраще рішення. Це також дозволяє налаштовувати більше, ніж можна зробити їхньою мовою сценаріїв за допомогою Javascript.

Calabash-Android : інструмент з відкритим кодом для функцій у стилі огірка. Плюси: писати функції мовою огірків, яка є зрозумілою для бізнесу, доменною мовою, що дозволяє описувати поведінку програмного забезпечення, не детально описуючи, як ця поведінка реалізована. Подібна, але не точна підтримка доступна для iOS в огірках-ios . Можливості запису не такі хороші, оскільки вони створюють двійковий вихід.

Кілька інших посилань:

  • Ось декілька додаткових порівнянь між Роботіумом, Monkeytalk та Калабашем. У ньому згадується TestDroid як ще одна можливість.
  • У цьому блозі згадується вищевказане плюс NativeDriver та Bot-bot.

3

Я створив інструмент запису та відтворення для Android і зробив його доступним на GitHub . Це легко налаштувати і використовувати, не вимагає програмування, працює проти реальних пристроїв (які не повинні бути вкоріненими) і автоматично зберігає знімки екрана під час відтворення тестів.


Це виглядає багатообіцяючим. Для тих, хто не бачить сенсу: це, здається, досить гарне рішення для тестування жестів (натискання, перетягування та інші речі)
tiktak

3

Перш за все, використовуйте "ActivityInstrumentationTestCase2", а не "InstrumentationTestCase", як свій базовий клас. Я використовую Robotium і регулярно тестую в різних видах діяльності. Я виявив, що мені потрібно вказати вхід в систему як загальний тип (і аргумент класу до конструктора).

Конструктор 'ActivityInstrumentationTestCase2' ігнорує аргумент пакета і не вимагає його. Конструктор, який бере пакет, застарілий.

З Javadocs: "ActivityInstrumentationTestCase2 (рядок pkg, клас ActivityClass) Цей конструктор застарілий. Замість нього використовуйте ActivityInstrumentationTestCase2 (клас)"

Використання рекомендованого базового класу дозволяє фреймворку обробляти певний шаблон, наприклад, починаючи свою діяльність. Це робиться за допомогою виклику 'getActivity ()', якщо це необхідно.


3

Знайшов це корисним із кількома модифікаціями. По-перше getInstrumentation().waitForIdleSync(), вилікує нестабільність, про яку говорить SingleShot, а також InstrumentationTestCaseмає lauchActivityфункцію, яка може замінити початкові лінії активності.


2

Ви можете зробити це так, щоб уникнути синхронізації часу очікування пластівців:

final Button btnLogin = (Button) getActivity().findViewById(R.id.button);
Instrumentation instrumentation = getInstrumentation();

// Register we are interested in the authentication activity...
Instrumentation.ActivityMonitor aMonitor = 
        instrumentation.addMonitor(mynextActivity.class.getName(), null, false);

getInstrumentation().runOnMainSync(new Runnable() {
         public void run() {
             btnLogin.performClick();
         }
     });

getInstrumentation().waitForIdleSync();

//check if we got at least one hit on the new activity
assertTrue(getInstrumentation().checkMonitorHit(aMonitor, 1)); 

1

Я працюю майже над тим самим, і я, мабуть, піду із варіацією прийнятої відповіді на це запитання, але під час пошуків рішення я натрапив на Calculuon ( gitHub ) .


0

Я не користувався ним особисто, але ApplicationTestCase, схоже, може бути тим, що ви шукаєте.


На жаль, немає прикладів, які б свідчили про те, що це так.
SingleShot

Так, схоже, ти маєш рацію ... було обманене ім’ям. Я не можу зрозуміти цього. Найкращий підхід, який я мав на сьогоднішній день, - це використовувати ActivityUnitTestCase позитрона, щоб перевірити, що розпочато наступну діяльність, але це не допоможе вам створити послідовні історії. Крім того, InstrumentationTestCase.launchActivity може дозволити вам розпочати довільну кількість дій, але я все ще намагаюся з’ясувати, що стосується інструментарію.
Ерік

0

Чи буде прийнятий підхід працювати з різними видами діяльності з різних додатків, підписаними різними сертифікатами? Якщо ні, то Роботіум - найкращий спосіб перевірити діяльність у рамках однієї програми.


0

Існує ще один спосіб зробити багаторазову діяльність за допомогою класу ActivityInstrumentation .. Це звичайний сценарій автоматизації ... Спочатку сфокусуйте будь-який об’єкт, який ви хочете, а потім надішліть ключ Simple, як цей зразок коду

button.requestFocus();
sendKeys(KeyEvent.KEYCODE_ENTER);

Єдине, що розуміння кожного виклику API нам допоможе.


0

Ця відповідь базується на прийнятій відповіді, але модифікована для вирішення проблеми з термінами, яка для мене стала послідовною після додавання близько півдюжини тестів. @ pajato1 отримує кредит на вирішення проблеми з термінами, про що йдеться у коментарях до прийнятих відповідей.

/**
 * Creates a test Activity for a given fully qualified test class name.
 *
 * @param fullyQualifiedClassName The fully qualified name of test activity class.
 *
 * @return The test activity object or null if it could not be located.
 */
protected AbstractTestActivity getTestActivity(final String fullyQualifiedClassName) {
    AbstractTestActivity result = null;

    // Register our interest in the given activity and start it.
    Log.d(TAG, String.format("Running test (%s) with main class: %s.", getName(), fullyQualifiedClassName));
    instrumentation = getInstrumentation();

    Intent intent = new Intent(Intent.ACTION_MAIN);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    intent.setClassName(instrumentation.getTargetContext(), fullyQualifiedClassName);
    // Wait for the activity to finish starting
    Activity activity = instrumentation.startActivitySync(intent);

    // Perform basic sanity checks.
    assertTrue("The activity is null!  Aborting.", activity != null);
    String format = "The test activity is of the wrong type (%s).";
    assertTrue(String.format(format, activity.getClass().getName()), activity.getClass().getName().equals(fullyQualifiedClassName));
    result = (AbstractTestActivity) activity;

    return result;
}

0

Спробуйте тестування інструменту Monkey

Крок 1:

відкрити термінал android studio (Tools-> open terminal)

Крок 2:

Для того, щоб використовувати мавпу, відкрийте командний рядок і просто перейдіть до наступного каталогу.

 export PATH=$PATH:/home/adt-bundle-linux-x86-20140702/sdk/platform-tools

Крок 3:

додайте цю команду мавпи в термінал і натисніть клавішу Enter ..

побачити магію у вашому емуляторі.

adb shell monkey -p com.example.yourpackage -v 500

500 - це підрахунок частоти або кількість подій, які слід надіслати на тестування.

ви можете змінити цей рахунок ..

Більше посилань,

http://www.tutorialspoint.com/android/android_testing.htm

http://androidtesting.blogspot.in/2012/04/android-testing-with-monkey-tool.html


Голосуючі повинні повідомити причину відмови ... це робочий код .. і офіційний метод тестування теж. Якщо є помилка, я готовий виправити свою відповідь ..
Ранджит Кумар,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.