본문

170726(수) - Fundamentals of Testing

Fundamentals of Testing


※ 180103(수) 기준, Google I/O '17 이후로 내용 업데이트 되었음


https://developer.android.com/training/testing/start/index.html


http://blog.dramancompany.com/2016/08/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C%EC%97%90-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EB%8F%84%EC%9E%85%ED%95%98%EA%B8%B0/



사전작업

- MVP

- Dagger2



MVP test

- Model layer

JUnit


- View layer

Espresso


- Presenter layer

Android Test Support Library(ATSL)



Android Test Support Library(ATSL)

- Android는 기본적으로 JUnit을 테스트에 사용

- JVM 상에서 Local Unit 테스트를 하거나 Android device에서 instrumented tests 가능



Test Types

- Local unit tests

Located at module-name/src/test/java/.


ㆍrun on the local JVM.

ㆍAndroid framework APIs에 접근할 수 없다.


- Instrumented tests

Located at module-name/src/androidTest/java/.


ㆍAndroid Emulator & Hardware에서 행해지는 test

ㆍ기기에서 실행되는 APK 안에 built 되어 있다.

ㆍSystem에서 test APK와 실행중인 app을 동일한 process에서 실행하므로 invoke method와 modify fields를 automate하게 test 가능하다.


ㆍBuilding Instrumented Unit Tests

mock objects로 충족되지 않으면 Android dependencies와 함께 complex unit test를 사용


ㆍAutomating User Interface Tests

single app or multiple app의 상호작용을 위해 user interface가 제대로 작동하는지 test


ㆍTesting App component Integrations

users do not directly interact, such as Service or Content Provider




- local unit tests & instrumented tests 는 JVM과 Android platform 에서 실행되는 test를 구별하는데 도움이 되는 용어일 뿐이다.


- Real testing type

TypeSubtypeDescription
Unit tests
Local Unit TestsUnit tests that run locally on the Java Virtual Machine (JVM). Use these tests to minimize execution time when your tests have no Android framework dependencies or when you can mock the Android framework dependencies.
Instrumented unit testsUnit tests that run on an Android device or emulator. These tests have access to Instrumentation information, such as the Context of the app you are testing. Use these tests when your tests have Android dependencies that mock objects cannot satisfy.
Integration Tests
Components within your app only

This type of test verifies that the target app behaves as expected when a user performs a specific action or enters a specific input in its activities. For example, it allows you to check that the target app returns the correct UI output in response to user interactions in the app’s activities. UI testing frameworks like Espresso allow you to programmatically simulate user actions and test complex intra-app user interactions.

Cross-app ComponentsThis type of test verifies the correct behavior of interactions between different user apps or between user apps and system apps. For example, you might want to test that your app behaves correctly when the user performs an action in the Android Settings menu. UI testing frameworks that support cross-app interactions, such as UI Automator, allow you to create tests for such scenarios.


Test APIs

- JUnit4

ㆍcommon setup

ㆍteardown

ㆍassertion operations


- @Test annotation 사용

- exercise and verify a single functionality


@RunWith(AndroidJUnit4.class)
@LargeTest
public class MainActivityInstrumentationTest {

   
@Rule
   
public ActivityTestRule mActivityRule = new ActivityTestRule<>(
           
MainActivity.class);

   
@Test
   
public void sayHello(){
        onView
(withText("Say hello!")).perform(click());

        onView
(withId(R.id.textView)).check(matches(withText("Hello, World!")));
   
}
}

- 다음 annotation을 사용하여 special processing test code sections 호출 가능

ㆍ@Before

- specify a block of code that contains test setup operations

- test class는 test 전에 이 코드 블럭을 invoke

- @Before class는 여러개 가질 수 있지만 calls order를 보장해 주지는 않는다. 


ㆍ@After

- specify a block of code that contains test tear-down operations

- 모든 test method 실행 후에 call 된다.

- multiple @After operations 정의 가능

- memory resource를 release


ㆍ@Test

- mark a test method

- single test class에는 여러개의 test method가 포함될 수 있으며, 각 method에는 이 annotation이 있다.


ㆍ@Rule

- flexibly add or redefine the behavior of test method in a reusable way

- Android Testing Support Library에서 제공하는 test rule classes 중 하나와 함께 use


ㆍ@BeforeClass

- test class가 한번만 invoke할 static method 지정

- useful for expensive operations such as connecting to a database


ㆍ@AfterClass

- 모든 test가 끝난후에 test class가 invoke할 static method 지정

- @BeforeClass block release에 유용


ㆍ@Test(timeout=)

- 일부 annotation은 set value 가능

- test는 pass 하였지만 timeout되면 fail 처리 된다.


ㆍmore

JUnit annotations Android annotations.



ㆍJUnit Assert class

- JUnit Assert class를 사용하여 object's state의 correctness를 확인한다.

- assert methods는 expect value와 actual value를 비교하고 다르면 exception을 throw 한다.

Assertion classes 



- Android Testing Support Library

- quickly build and run test code 를 위한 API set 지원

- JUnit 4 and functional UI tests

- automate test를 위한 instrumentation-based APIs 가 들어 있다.


- AndroidJUnitRunner

ㆍJUnit 4 compatible test runner for Android

AndroidJUnitRunner


- Espresso

ㆍUI testing framework

ㆍApp 내의 functional UI testing에 적합


- UI Automator

ㆍSystem 및 installed app 사이의 Cross-app functional UI testing에 적합



- Assertion classes

- Support Library APIs는 JUnit을 확장했으므로, assertion을 사용하여 test result를 볼 수 있다.

- actual value와 expected value를 비교하여 테스트가 fail하면 AssertionException을 throw 한다.

- more convenient than logging and better test performance

- 개발 환경을 단순화하고 더 유연한 테스트를 위해 Hamcrest libary를 사용해야 한다.

Hamcrest library



- Monkey and monkeyrunner

- Android SDK는 functional-level app testing을 위한 두개의 tools를 가지고 있다.


- Monkey

ㆍkeystrokes, touch, gesture의 pseudo-random streams를 장치로 보내는 command line tool 이다.

ㆍAndroid Debug Bridge(adb) tool을 사용하여 실행

ㆍstress-test your app

ㆍreport back errors 

ㆍ동일한 random number seed를 사용하여 tool을 multiple 실행하여 event stream을 반복 가능


-  monkeyrunner

ㆍPython으로 작성된 test programs를 위한  API 와 execution enviroment

ㆍAPI includes connecting device, installing and uninstalling packages, taking screenshots, compare two images, running a test package

ㆍAPI를 사용하여 wide range of large, powerful, and complex test가 가능하다.

monkeyrunner command-line tool로 API를사용하는 프로그램 실행



Guide for Building Android Tests

- Building Local Unit Tests

local JVM에서 실행되는 no dependencies or simple dependencies mock, unit tests build


- Building Instrumented Unit Tests

hardware or emulator device에서 mock objects로는 만족되어지지 않는 Android dependencies unit test를 build


- Automating User Interface Tests

single app or interactions across multiple apps에서 상호작용을 위해서 user interface가 올바르게 작동하는지 확인하는 test


- Testing app Component Integrations

user가 not directly interact component (ex. service, content provider) 의 동작을 verify


- Testing Display Performance

consistently smooth user experience를 보장하기 위해 UI performence를 측정하는 test 




※ 180103(수) 기준, 추가된 내용


Testing

- rapid feedback

- early failure detection

- safer code refactoring

- stable development velocity


Use an iterative development workflow

- App이 확장됨에 따라 다양한 기능이 추가될 수 있다.

- 따라서 다양하고 포괄적인 testing strategy가 필요

- 반복적인 developing에서는 new and adding test case

- new feature design 시에 unit of responsibility를 고려해야 한다.

- 각 unit에 대해서 unit test를 작성

- unit test는 거의 모든 interactions를 exhaust해야 한다.

- 길고 느린 UI test cycle, 짧고 빠른 development cycle

- all use case가 완료될때 까지 반복


The testing development cycle consists of writing a failing unit
           test, writing code to make it pass, and then refactoring. The entire
           feature development cycle exists inside one step of a larger,
           UI-based cycle.



Understanding the Testing Pyramid

- small tests

・ production system과는 별도로 run 가능한 tests

・ 일반적으로 every major component를 mock test하며 machine에서 빠르게 실행되어야 한다.


- medium tests

・ small and large 사이의 integration tests

・ emulator or real device에서 run


- large tests

・ integration and UI tests

・ key end-user tasks work가 emulators or real devices에서 제대로 동작하는지 확인



- small test는 fast and focused, failures quickly 처리 가능하지만, low-fidelity and self-contained 이므로 confidence를 주기는 어렵다

- large test에서는 tradeoffs이다.

- 각 test category characteristics가 다르기 때문에 각 layer에서 test를 포함해야 한다.

- 70% small, 20% medium, and 10% large 가 적당하다.

 Test-Driven Development on Android 


Write small tests

- app's functionality가 add or change되면 해당 기능에 대해 unit test를 만들고 running하여 기능이 의도한대로 작동하는지 확인

- device or emulator에서도 unit test가 가능하지만 development enviroment 에서의 unit test 및 adding stubbed and mocked methods interact with android system 하는것이 quicker and easier 하다


Robolectric

- unit test가 android framework와 more extensively하게 interact해야하는 경우 사용

- java-based logic stubs

- android on-device tests와 fidelity는 비슷하지만 더 quickly executing

- android flatform 지원

・ android 4.1 (API level 16) and higher

・ component lifecycles

・ event loops

・ all resources


http://robolectric.org/configuring/



Mock objects

- android.jar의 수정된 버전에 대해 unit test를 running하여 interact android framework element를 모니터링 가능

- JAR 파일에는 code가 없으므로 calls to android framework throw exceptions by default

- interact with android system code element를 테트하려면 Mockito를 사용하여 mock object를 configure해야한다.

- code resource에 대한 references or complex interactions with android framework 이 포함되어 있는 경우 Robolectric과 같은 다른 unit testing을 사용해야 한다.



Instrumented unit tests

- unit test on physical device or emulator

- doesn't involve mocking or stubbing of framework

- local unit tests보다 significantly slower execution time 이므로 actual device hardware app's behavior evaluate 에만 쓰도록 해야한다.



Write medium tests

- development environment unit test후에 실제 장치에서 제대로 동작하는지 확인하는 작업

- app이 multiple unit을 coordinates하는 방식을 평가하지만 full app을 test하지는 않는다

- physical device보다 여러가지 환경을 테스트 가능한 emulate device나 cloud-based service에서 테스트를 진행하는것이 좋다.



Write large tests

- UI, business logic, data layer 등 전체 workflows를 테스트하는것도 중요

- app의 크기가 작으면 전체 app의 functionality를 평가할수있는 large test가 하나만 필요할 수 있다.

- otherwise, team ownership, functional verticals or user goals 별로 large tests를 나눠야 한다.

- 각 large test별로 UI기능을 테스트 하는 medium test도 필요


AndroidJUnitRunnerclass defines instrumentation-based  JUnit test runner that JUint3-or JUnit 4-style test classes on Android devices

- AdnroidUnintRunner는 ATSL의 아래 tools도 지원한다.


JUnit4 Rules

- ATSL에는 lifecycle관리를 위한 code가 포함되어 있다.

JUnit4 Rules



Espresso

- interacting tasks를 automating하면서 asynchronous tasks를 synchronizes한다.

- View object에 대한 작업 수행

- app's precess boundaries를 cross하는 workflows completing. (Android 8.0 (API level 26) and higher)

- accessibility user가 app을 사용하는 방식 평가

- RecyclerView object안의 locating and activating item

- outgoing intents validating state 

- webview object내의 DOM구조 확인

- app내부의 long-running background operations tracking


 Espresso 



UI Automator

Caution: We recommend testing your app using UI Automator only when your app must interact with the system to fulfill a critical use case. Because UI Automator interacts with system apps and UIs, you need to re-run and fix your UI Automator tests after each system update. Such updates include Android platform version upgrades and new versions of Google Play services.

As an alternative to using UI Automator, we recommend adding hermetic tests or separating your large test into a suite of small and medium tests. In particular, focus on testing one piece of inter-app communication at a time, such as sending information to other apps and responding to intent results. The Espresso-Intents tool can help you write these smaller tests.

Automator 



Android Test Orchestrator

- own sandbox에서 UI test running

Android Test Orchestrator 


공유

댓글