Android
UI 테스트 작성 - Android
수색…
소개
이 문서의 초점은 안드로이드 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);
}
}
}
};
}
}