수색…


소개

이 문서의 초점은 안드로이드 UI 및 통합 테스트를 작성하는 방법과 목표를 표현하는 것입니다. Espresso 및 UIAutomator는 Google에서 제공하므로 이러한 도구와 해당 래퍼 (예 : Appium, Spoon 등)에 중점을 두어야합니다.

통사론

  • 유휴 리소스
  • String getName () - 유휴 리소스의 이름을 반환합니다 (등록의 로깅 및 동일시).
  • boolean isIdleNow () - 자원이 현재 유휴 상태이면 true를 반환합니다.
  • void registerIdleTransitionCallback (IdlingResource.ResourceCallback callback) - 지정된 IdlingResource.ResourceCallback을 자원에 등록합니다.

비고

JUnit 규칙 :

MockWebServer 예제와 ActivityTestRule에서 볼 수 있듯이 이들 모두 JUnit 룰 카테고리에 속하며, 사용자는 자신을 작성하여 각 행동을 정의하는 각 테스트마다 실행해야합니다. @see : https://github.com/junit-team/junit4/ 위키 / 규칙

Appium

매개 변수

문서 버그가 해결 될 때까지 매개 변수에 몇 가지 문제가 있습니다.

매개 변수 세부
클래스 activityClass 시작할 활동
initialTouchMode 시작시 활동을 터치 모드로 설정해야합니다. https://android-developers.blogspot.de/2008/12/touch-mode.html
launchActivity Activity가 Test 메서드마다 한 번 실행되어야하는 경우 true입니다. 첫 번째 Before 메서드보다 먼저 시작되고 마지막 After 메서드보다 이후에 종료됩니다.

MockWebServer 예제

액티비티, 프래그먼트 및 UI가 백그라운드 처리를 필요로하는 경우, 사용하기 좋은 MockWebServer가 안드로이드 장치에서 실행되므로 UI를 닫고 테스트 할 수있는 환경을 제공합니다.

https://github.com/square/okhttp/tree/master/mockwebserver

첫 번째 단계는 gradle 종속성을 포함합니다.

testCompile 'com.squareup.okhttp3:mockwebserver:(insert latest version)'

이제 모의 서버를 실행하고 사용하는 단계는 다음과 같습니다.

  • 모의 서버 객체 만들기
  • 특정 주소와 포트에서 시작하십시오 (일반적으로 localhost : portnumber).
  • 특정 요청에 대한 응답을 대기열에 넣음
  • 시험을 시작하다.

이것은 mockwebserver의 github 페이지에서 잘 설명되어 있지만 우리의 경우 우리는 모든 테스트에 대해 더 좋고 재사용 가능한 것을 원합니다. JUnit 규칙은 다음과 같이 훌륭하게 작동합니다 :

/**
 *JUnit  rule that starts and stops a mock web server for test runner
*/
 public class MockServerRule extends UiThreadTestRule {

 private MockWebServer mServer;

 public static final int MOCK_WEBSERVER_PORT = 8000;

    @Override
    public Statement apply(final Statement base, Description description) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                startServer();
                try {
                    base.evaluate();
                } finally {
                    stopServer();
                }
            }
        };
    }

    /**
     * Returns the started web server instance
     *
     * @return mock server
     */
    public MockWebServer server() {
        return mServer;
    }

    public void startServer() throws IOException, NoSuchAlgorithmException {
        mServer = new MockWebServer();
        try {
            mServer(MOCK_WEBSERVER_PORT);
        } catch (IOException e) {
            throw new IllegalStateException(e,"mock server start issue");
        }
    }

    public void stopServer() {
        try {
            mServer.shutdown();
        } catch (IOException e) {
            Timber.e(e, "mock server shutdown error”);
        }
    }
}

이제 우리는 앞의 예에서와 똑같은 활동을한다고 가정합니다.이 경우 버튼 앱을 누르면 앱이 네트워크에서 무언가를 가져옵니다. https://someapi.com/name

이것은 당신이 입력 한 NAME + 텍스트와 같은 스낵바 텍스트에서 연결될 어떤 텍스트 문자열을 반환합니다.

/**
* Testing of the snackbar activity with networking.
**/
@RunWith(AndroidJUnit4.class)
@LargeTest
public class SnackbarActivityTest{
    //espresso rule which tells which activity to start
    @Rule
    public final ActivityTestRule<SnackbarActivity> mActivityRule = 
        new ActivityTestRule<>(SnackbarActivity.class, true, false);

    //start mock web server
    @Rule
    public final MockServerRule mMockServerRule = new MockServerRule();

    @Override
    public void tearDown() throws Exception {
       //same as previous example
    }
    
    @Override
    public void setUp() throws Exception {
       //same as previous example

       **//IMPORTANT:** point your application to your mockwebserver endpoint e.g.
       MyAppConfig.setEndpointURL("http://localhost:8000");
    }
    
    /**
    *Test methods should always start with "testXYZ" and it is a good idea to 
    *name them after the intent what you want to test
    **/
    @Test
    public void testSnackbarIsShown() {
        //setup mockweb server
        mMockServerRule.server().setDispatcher(getDispatcher());

        mActivityRule.launchActivity(null);
        //check is our text entry displayed and enter some text to it
        String textToType="new snackbar text";
        onView(withId(R.id.textEntry)).check(matches(isDisplayed()));
        //we check is our snackbar showing text from mock webserver plus the one we typed
        onView(withId(R.id.textEntry)).perform(typeText("JazzJackTheRabbit" + textToType));
        //click the button to show the snackbar
        onView(withId(R.id.shownSnackbarBtn)).perform(click());
        //assert that a view with snackbar_id with text which we typed and is displayed
        onView(allOf(withId(android.support.design.R.id.snackbar_text), 
        withText(textToType))) .check(matches(isDisplayed()));
    }
    
     /**
     *creates a mock web server dispatcher with prerecorded requests and responses
     **/
    private Dispatcher getDispatcher() {
        final Dispatcher dispatcher = new Dispatcher() {
            @Override
            public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
                if (request.getPath().equals("/name")){
                    return new MockResponse().setResponseCode(200)
                            .setBody("JazzJackTheRabbit");
                }
                throw new IllegalStateException("no mock set up for " + request.getPath());
            }
        };
        return dispatcher;
    }

Dispatcher를 일종의 작성기로 배치하여 화면에 새로운 응답을 쉽게 추가하고 추가 할 수 있도록 제안합니다. 예

 return newDispatcherBuilder()
            .withSerializedJSONBody("/authenticate", Mocks.getAuthenticationResponse())
            .withSerializedJSONBody("/getUserInfo", Mocks.getUserInfo())
            .withSerializedJSONBody("/checkNotBot", Mocks.checkNotBot());

IdlingResource

유휴 리소스의 힘은 sleep() 으로 끝내기 위해 일부 응용 프로그램의 처리 (네트워킹, 계산, 애니메이션 등)를 기다릴 필요가 없기 때문에 박테리아가 벗겨 지거나 테스트가 길어질 수 있습니다. 공식 문서는 여기 에서 찾을 수 있습니다 .

이행

IdlingResource 인터페이스를 구현할 때해야 할 세 가지가 있습니다.

  • getName() - 유휴 리소스의 이름을 반환합니다.
  • isIdleNow() - xyz 객체, 작업 등이 현재 유휴 상태인지 여부를 확인합니다.
  • registerIdleTransitionCallback ( IdlingResource.ResourceCallback callback) - 객체가 유휴 상태로 전환 될 때 호출해야하는 콜백을 제공합니다.

이제는 앱에 의존하기 때문에 자신의 로직을 만들고 앱이 유휴 상태 일 때와 그렇지 않을 때를 결정해야합니다. 아래에서 간단한 예제를 찾아서 어떻게 작동하는지 보여줍니다. 온라인으로 다른 예제가 있지만 특정 앱 구현은 특정 유휴 리소스 구현을 가져옵니다.

노트

  • IdlingResources 를 앱의 코드에 넣는 Google 예제가 있습니다. 이러지 마. 그들은 아마도 그것이 작동하는 방식을 보여주기 위해 아마도 그것을 배치했을 것입니다.
  • 코드를 깨끗하게 유지하고 책임의 단일 원칙을 유지하는 것은 여러분의 책임입니다!

이상한 일을하고 조각이로드되는 데 시간이 오래 걸리므로 조각에서 자원을 찾을 수 없으므로 에스프레소 검사가 실패하게됩니다 (활동을 작성하는 방법을 변경해야하며 속도를 높이기 위해). 그러나 어떤 경우 든 간단하게 유지하려면 다음 예는 어떻게 표시되어야하는지 보여줍니다.

예제 Idling 리소스는 두 개의 객체를 얻습니다.

  • 액티비티에 연결하여 찾기 위해 기다려야하는 조각의 태그 입니다.
  • 프래그먼트 를 찾는 데 사용되는 FragmentManager 객체입니다.
/**
 * FragmentIdlingResource - idling resource which waits while Fragment has not been loaded.
 */
public class FragmentIdlingResource implements IdlingResource {
    private final FragmentManager mFragmentManager;
    private final String mTag;
    //resource callback you use when your activity transitions to idle
    private volatile ResourceCallback resourceCallback;

    public FragmentIdlingResource(FragmentManager fragmentManager, String tag) {
        mFragmentManager = fragmentManager;
        mTag = tag;
    }

    @Override
    public String getName() {
        return FragmentIdlingResource.class.getName() + ":" + mTag;
    }

    @Override
    public boolean isIdleNow() {
        //simple check, if your fragment is added, then your app has became idle
        boolean idle = (mFragmentManager.findFragmentByTag(mTag) != null);
        if (idle) {
            //IMPORTANT: make sure you call onTransitionToIdle
            resourceCallback.onTransitionToIdle();
        }
        return idle;
    }

    @Override
    public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
        this.resourceCallback = resourceCallback;
    }
}

IdlingResource 작성 IdlingResource 이제 어딘가에 사용해야합니다.

용법

전체 테스트 클래스 설정을 건너 뛰고 테스트 케이스가 어떻게 보이는지 살펴 보겠습니다.

@Test
public void testSomeFragmentText() {
    mActivityTestRule.launchActivity(null);
   
    //creating the idling resource
    IdlingResource fragmentLoadedIdlingResource = new FragmentIdlingResource(mActivityTestRule.getActivity().getSupportFragmentManager(), SomeFragmentText.TAG);
    //registering the idling resource so espresso waits for it
    Espresso.registerIdlingResources(idlingResource1);
    onView(withId(R.id.txtHelloWorld)).check(matches(withText(helloWorldText)));

    //lets cleanup after ourselves
    Espresso.unregisterIdlingResources(fragmentLoadedIdlingResource);
}

JUnit 규칙과의 결합

이것은 어려운 것이 아닙니다. 유휴 리소스를 JUnit 테스트 규칙의 형태로 적용 할 수도 있습니다. 예를 들어, Volley가 들어있는 SDK가 있고 Espresso가 SDK를 기다리고 싶다고합시다. 각 테스트 케이스를 거치거나 설정에서 적용하는 대신 JUnit 규칙을 작성하여 다음과 같이 작성할 수 있습니다.

@Rule
public final SDKIdlingRule mSdkIdlingRule = new SDKIdlingRule(SDKInstanceHolder.getInstance());

이제 이것이 예제이기 때문에 당연한 것으로 생각하지 마십시오. 여기에있는 모든 코드는 가상이며 데모 목적으로 만 사용됩니다.

public class SDKIdlingRule implements TestRule {
    //idling resource you wrote to check is volley idle or not
    private VolleyIdlingResource mVolleyIdlingResource;
    //request queue that you need from volley to give it to idling resource
    private RequestQueue mRequestQueue;

    //when using the rule extract the request queue from your SDK
    public SDKIdlingRule(SDKClass sdkClass) {
        mRequestQueue = getVolleyRequestQueue(sdkClass);
    }

    private RequestQueue getVolleyRequestQueue(SDKClass sdkClass) {
        return sdkClass.getVolleyRequestQueue();
    }

    @Override
    public Statement apply(final Statement base, Description description) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                //registering idling resource
                mVolleyIdlingResource = new VolleyIdlingResource(mRequestQueue);
                Espresso.registerIdlingResources(mVolleyIdlingResource);
                try {
                    base.evaluate();
                } finally {
                    if (mVolleyIdlingResource != null) {
                        //deregister the resource when test finishes
                        Espresso.unregisterIdlingResources(mVolleyIdlingResource);
                    }
                }
            }
        };
    }
}


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow