여러 활동에서 Android 애플리케이션을 어떻게 테스트합니까?


80

우리는 많은 활동에 분산 된 많은 화면과 워크 플로로 구성된 복잡한 Android 애플리케이션을 구축하고 있습니다. 우리의 워크 플로우는 은행의 ATM 기계에서 볼 수있는 것과 유사합니다. 예를 들어 사용자의 선택에 따라 다른 활동으로 전환 할 수 Activity있는 주 메뉴로 전환하는 로그인 Activity이 있습니다.

워크 플로가 너무 많기 때문에 워크 플로를 처음부터 끝까지 테스트 할 수 있도록 여러 활동에 걸쳐 자동화 된 테스트를 만들어야합니다. 예를 들어, ATM 예제를 사용하여 유효한 PIN을 입력하고, 우리를 메인 메뉴로 보내는 지 확인하고, 현금 인출을 선택하고, 우리가 현금 인출 화면에 있는지 등을 확인하고, 결국 우리 자신을 찾을 수 있습니다. 메인 메뉴로 돌아가거나 "로그 아웃"했습니다.

우리는 Android (예 ActivityInstrumentationTestCase2) 및 Positron 과 함께 제공되는 테스트 API를 가지고 놀았 지만 둘 다 하나의 범위를 넘어서 테스트 할 수없는 것처럼 보 Activity였으며 이러한 도구에서 일부 단위 테스트를위한 유틸리티를 찾을 수는 있지만 이겼습니다. 여러 활동에 걸쳐있는 테스트 시나리오에 대한 우리의 요구를 충족하지 못합니다.

우리는 xUnit 프레임 워크, 스크립팅, GUI 레코더 / 재생 등에 대해 열려 있으며 조언을 주시면 감사하겠습니다.


2
Android 4.1부터 활동 및 전체 시스템을 테스트 할 수있는 Android의 새로운 테스트 프레임 워크가 있습니다. developer.android.com/tools/testing/testing_ui.html
Christopher Orr

1
Robotium 은이 요구 사항도 몇 줄로 충족 할 것입니다.
Dori 2013 년

답변:


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);

나는 약 4 개의 활동을 깊이 테스트했고 내가 말했듯이 접근 방식이 효과가 있다는 것에 만족한다. 가끔 내가 완전히 파악하지 못한 타이밍 문제가있는 것 같다. 활동 전반에 걸친 다른 테스트 방법에 대해 듣고 싶습니다.


3
타이밍 문제로 인해 실패 할 때 자동으로 테스트를 반복하도록 FlakyTest 주석을 추가 할 수 있습니다. 실제로 해결책은 아니지만 일부 상황에서 실행 가능한 해결 방법입니다.
Carl Manaster

작성해 주셔서 감사합니다! 내 테스트를 위해 ActivityMonitors의 기능을 가진 무언가를 찾고있었습니다. 나는 그들을 찾을 수 없었습니다.
Peter Ajtai

내가 아는 한, 위에서 수행 한 작업은 사용할 수 없습니다ActivityInstrumentationTestCase2
ericn

어떤 조건에서 어떤 아이디어, 'getInstrumentation (). waitForIdleSync ();' 무한 루프에 들어 갈까요? 프로세서 보드를 실행하는 Android 4.4.2_r2에서 CTS 테스트를 실행하는 동안이 문제가 발생합니다.
ArunJTS

내 아들 @ pajato1이 당신의 타이밍 문제를 발견하고 고쳤다 고 생각합니다. 그의 수정으로 내 문제가 해결되었습니다. 그가 말한 내용은 다음과 같습니다. "Javadoc에서 Instrumentation.startActivitySync ()가 새 활동이 준비 될 때까지 차단 된 다음이를 반환하는 것을 확인했습니다. 따라서 모니터가 필요하지 않은 것 같습니다. 제거하면 이것이 올바른 것으로 입증되었습니다. 이론은 Monitor가 startActivitySync ()에 의해 생성 된 Activity가 경합 상태로 인해 어떤 경우에 다시 시작되도록했다는 것입니다. 나는 Android 소스 코드를 읽는 데 시간을 보냈지 만 경합 상태의 원인으로 나에게 튀어 나온 것은 없습니다. "
pajato0

22

Robotium 'Android 애플리케이션의 자동 블랙 박스 테스트를 즉시 사용할 수있는 Android 계측 테스트로 가능한 것보다 훨씬 더 빠르고 쉽게 테스트 할 수 있도록 만들어진 오픈 소스 테스트 프레임 워크'를 살펴보세요
.

홈페이지 : http://www.robotium.org/
출처 : http://github.com/jayway/robotium

Robotium 프로젝트는 제가 근무하는 회사에서 관리합니다.


안녕하세요, 이것에 대한 레코더 도구가 있습니까? 나는 많은 웹 사이트를 확인하고 스크립트를 기록하고 실행하는 testdroid를 찾았습니다. 불행히도 그것은 프리웨어가 아닙니다. 레코딩 프로세스를 수행하는 프리웨어를 아십니까?
thndrkiss 2011 년

@thndrkiss : 그런 도구에 대해 모르겠습니다. Robotium 포럼에 질문을하면 더 나은 답변을 얻을 수 있습니다.
Jonas Söderström

2
Robotium은 생명의 은인입니다. 테스트를 작성하기가 매우 쉬워 질 것입니다 (기본적으로 평이한 영어로 말하고 있습니다 : 이것을 클릭하고 뒤로 버튼을 누르십시오.). 아무 것도 테스트 할 수 있지만 작은 세부 사항을 알 필요는 없습니다. 최소한 두 가지 주요 이점이 있습니다. 소스 코드가없는 앱을 테스트 할 수 있으며 UI에 의존하여 매우 견고합니다 (컨트롤러 / 모델을 뷰보다 훨씬 많이 변경합니다 ...)
tiktak

8

항상 Robotium을 사용할 수 있습니다. Selenium과 마찬가지로 Android 용 블랙 박스 테스트를 지원합니다. Robotium.org에서 찾을 수 있습니다.


1
마지막으로 확인한 Robotium은 여러 활동에서 사용할 수 없습니다. 지금 해결 되었습니까? stackoverflow.com/questions/3840034/…
user77115 2011-08-09

3
동일한 프로세스에 속하는 한 항상 여러 활동에서 작동했습니다.
Renas

4

선도적 인 자동화 기능 테스트 도구 중 일부를 언급하지 않은 것에 놀랐습니다 . Robotium과 비교하면 Java 코드를 작성할 필요가 없습니다.

MonkeyTalk : Gorilla Logic 사가 지원하는 오픈 소스 도구입니다. 장점 : 기술에 익숙하지 않은 사용자에게 더 쉽게 녹음 및 고급 스크립팅 언어를 제공하며 크로스 플랫폼 (iOS 포함)입니다. 이러한 이점을 요구 사항으로 고려할 때 이것이 최상의 솔루션이라는 것을 알았습니다. 또한Javascript를 사용하여 스크립팅 언어로 수행 할 수있는 것 이상의 사용자 정의 를허용합니다.

Calabash-Android : Cucumber 스타일 기능을위한 오픈 소스 도구입니다. 장점 : 비즈니스에서 읽을 수있는 Gherkin 언어로 기능을 작성하고 해당 동작이 구현되는 방법을 자세히 설명하지 않고도 소프트웨어의 동작을 설명 할 수 있습니다. cucumber-ios 에서 iOS에 대해 유사하지만 정확한 지원이 제공되지 않습니다. 기록 기능은 바이너리 출력을 생성하기 때문에 좋지 않습니다.

몇 가지 다른 참조 :

  • 다음은 Robotium, Monkeytalk 및 Calabash의 몇 가지 추가 비교 입니다. 또 다른 가능성으로 TestDroid 를 언급합니다 .
  • 블로그 는 위의 내용과 NativeDriver 및 Bot-bot을 언급합니다.

3

Android 용 녹음 및 재생 도구를 만들어 GitHub에서 사용할 수 있도록했습니다 . 구성 및 사용이 쉽고 프로그래밍이 필요하지 않으며 실제 장치 (루팅 할 필요가 없음)에서 실행되며 테스트를 재생할 때 스크린 샷을 자동으로 저장합니다.


이것은 유망 해 보입니다. 지점이 표시되지 않는 사람들을 위해 :이 테스트 동작에 꽤 좋은 솔루션 (도청 끌어 다른 것들) 것 같다
tiktak

3

우선, 'InstrumentationTestCase'가 아닌 'ActivityInstrumentationTestCase2'를 기본 클래스로 사용하십시오. 저는 Robotium을 사용하고 정기적으로 여러 활동을 테스트합니다. 로그인 활동을 제네릭 유형 (및 생성자에 대한 클래스 인수)으로 지정해야한다는 것을 알았습니다.

'ActivityInstrumentationTestCase2'생성자는 패키지 인수를 무시하고 필요하지 않습니다. 패키지를받는 생성자는 더 이상 사용되지 않습니다.

Javadocs에서 : "ActivityInstrumentationTestCase2 (String pkg, Class activityClass)이 생성자는 더 이상 사용되지 않습니다. 대신 ActivityInstrumentationTestCase2 (Class)를 사용하십시오."

권장되는 기본 클래스를 사용하면 프레임 워크가 활동 시작과 같은 특정 상용구를 처리 할 수 ​​있습니다. 필요한 경우 '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 2009

그래, 네 말이 맞는 것 같군 ... 이름에 속았다. 나는 이것을 이해할 수 없다. 내가 지금까지 얻은 최선의 접근 방식은 positron의 ActivityUnitTestCase를 사용하여 다음 활동이 시작되었는지 확인하는 것이지만 일관된 스토리를 구축하는 데 도움이되지 않습니다. 또는 InstrumentationTestCase.launchActivity를 사용하면 임의의 수의 활동을 시작할 수 있지만 여전히 Instrumentation 항목을 파악하려고합니다.
Eric

0

승인 된 접근 방식은 서로 다른 인증서로 서명 된 서로 다른 애플리케이션의 서로 다른 활동에서 작동합니까? 그렇지 않은 경우 Robotium은 동일한 애플리케이션 내에서 활동을 테스트하는 가장 좋은 방법입니다.


0

ActivityInstrumentation Class를 사용하여 여러 활동을 수행하는 또 다른 방법이 있습니다. 일반적인 자동화 시나리오입니다. 먼저 원하는 객체에 초점을 맞춘 다음 샘플 코드로 Simple 키를 보냅니다.

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

유일한 것은 모든 API 호출이 우리에게 도움이 될 것이라는 것을 이해하는 것입니다.


0

이 답변은 허용 된 답변을 기반으로하지만 약 6 개의 테스트를 추가 한 후 일관성이있는 타이밍 문제를 해결하기 위해 수정되었습니다. @ 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 단계:

안드로이드 스튜디오 터미널 열기 (도구-> 터미널 열기)

2 단계:

monkey를 사용하려면 명령 프롬프트를 열고 다음 디렉토리로 이동하십시오.

 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


비추천 인은 비추천 이유를 말해야합니다. 이것은 작동하는 코드입니다. 그리고 공식적인 테스트 방법이기도합니다. 만약 내가 내 대답을 정정 할 준비가 어떤 실수 ..
란 지트 쿠마르
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.