Kotlin으로 ☕ Espresso UI 테스트 기반 잘 다지는 법 #1 - 테스트 케이스
⚙️ 기본 설정
Espresso (에스프레소)
는 구글에서 제공하는 안드로이드 UI 테스트 라이브러리이다. 3.x 버전부터는 AndroidX Library
에 통합되어 제공되고 있다. 기본적인 설정은 안드로이드 개발 코드 안에서 직접 환경설정할 수 있다.
build.gradle
android {
defaultConfig {
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
}
// https://developer.android.com/jetpack/androidx/releases/test
def androidxTest = '1.2.0'
def espresso = '3.2.0'
dependencies {
...
androidTestImplementation 'androidx.test:core:${androidxTest}'
androidTestImplementation 'androidx.test:runner:${androidxTest}'
androidTestImplementation 'androidx.test:rules:${androidxTest}'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:${espresso}'
androidTestImplementation 'androidx.test.espresso:espresso-intents:${espresso}'
androidTestImplementation 'androidx.test.espresso:espresso-contrib:${espresso}'
androidTestImplementation 'androidx.test.espresso:espresso-idling-resource:${espresso}'
androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'
}
🚀 기본을 넘어서, 실용적인 코드로
이미 안드로이드 개발 문서에는 처음 에스프레소 테스트를 설정하는 방법이 잘 나와있다. 블로그도 여럿 있다. 테스트 자동화에서 가장 중요한 부분은 Stability (안정성)
과 Maintainability (유지/보전성)
이다. 계속해서 변화하는 앱에 따라 테스트도 관리해주어야 한다. 이를 보다 쉽고 빠르게 하기 위해, 기본적인 부분보다는, 더 실용적으로 기반을 다질 수 있는 코드를 보여주려 한다.
테스트를 Pass도 중요하지만, Fail했을때 디버깅 또한 중요하다. 이를 수월하게 해주는 첫 스텝은 바로 스크린샷이다.
FailureTestWatcher.kt
class FailureTestWatcher : TestWatcher() {
override fun failed(e: Throwable?, description: Description) = takeScreenshot(description)
private val screenshotFolder = File("/sdcard/screenshots")
private fun prepareScreenshotPath() {
runCatching {
screenshotFolder.mkdirs()
}.onFailure {
Log.w("Failed to create the screenshot folder $screenshotFolder: $it")
}
}
private fun takeScreenshot(description: Description) {
prepareScreenshotPath()
val capture = Screenshot.capture()
val imageFile = File(screenshotFolder, "${description.methodName}_${Date().time}.png")
runCatching {
BufferedOutputStream(FileOutputStream(imageFile)).use {
capture.bitmap.compress(capture.format, 100, it)
}
}.onFailure {
Log.w("Failed to save a screenshot on test failure")
}
}
}
위와 같이 테스트 실패 시 스크린샷을 찍는 Class를 설정해준다. 이를 기본 테스트 베이스를 설정해놓고, @Rule 훅에 연결해주면 된다.
TestBase.kt
abstract class TestBase {
@Rule @JvmField
val activityRule = ActivityTestRule(MainActivity::class.java)
@Rule @JvmField
val test = TestName()
@Rule @JvmField
val failureWatcher = FailureTestWatcher()
@Before
internal fun internalSetup() {
setUp()
}
abstract fun setUp()
@After
internal fun internalTearDown() {
tearDown()
}
abstract fun tearDown()
}
또한, Context를 설정해줘, 테스트 수행 시 activity 레벨에서 관리가 되고, 정보를 관리할 수 있도록 설정해주면, 관리가 수월해진다.
TestContext.kt
private const val EXPLICIT_WAIT_TIME = 1000L
class TestContext(private val instrumentation: Instrumentation) {
val currentActivity: Activity
get() {
var firstResumedActivity: Activity? = null
onIdle()
instrumentation.runOnMainSync {
firstResumedActivity =
ActivityLifecycleMonitorRegistry
.getInstance()
.getActivitiesInStage(RESUMED)
.last()
}
return firstResumedActivity!!
}
fun waitForActivityToAppear(activity: String) {
while (activity !in currentActivity.localClassName) {
runCatching {
SystemClock.sleep(EXPLICIT_WAIT_TIME)
}
}
}
fun waitForActivityToDisappear(activity: String) {
while (activity in currentActivity.localClassName) {
runCatching {
SystemClock.sleep(EXPLICIT_WAIT_TIME)
}
}
}
}
이를 매 테스트 설정 시 관리해주면 테스트 코드를 짜기 수월해진다. 안드로이드 설정 Permission 관리도 여기서 가능하다.
TestBase.kt
abstract class TestBase {
lateinit var context: TestContext
@Rule @JvmField
val activityRule = ActivityTestRule(MainActivity::class.java)
...
@Rule @JvmField
var grantPermissionRule: GrantPermissionRule =
GrantPermissionRule.grant(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE
)
@Before
internal fun internalSetup() {
context = TestContext(InstrumentationRegistry.getInstrumentation())
setUp()
}
...
}
이 베이스를 새로운 테스트 class를 생성할 때마다 베이스로 설정해주면, 한 곳에서 관리가 되는 편리함이 생긴다.
SampleTest.kt
class SampleTest : TestBase() {
override fun setUp() {
...
}
@Test
fun scenario1() {
...
}
}
'👨🏻💻 QA이야기 > 📱 모바일자동화' 카테고리의 다른 글
Kotlin으로 ☕ Espresso UI 테스트 기반 잘 다지는 법 #2 - Matchers (0) | 2020.04.19 |
---|---|
Swift로 🍏 UI XCTest 기반 잘 다지는 법 #2 - Action (0) | 2020.04.04 |
Swift로 🍏 UI XCTest 기반 잘 다지는 법 #1 - 테스트 케이스 (0) | 2020.04.04 |
🥒 Cucumber 이해하고 잘 쓰는 방법 (0) | 2020.04.03 |
🗳️ QA의 모바일 자동화를 위한 개발환경 (0) | 2020.04.01 |
댓글
이 글 공유하기
다른 글
-
Kotlin으로 ☕ Espresso UI 테스트 기반 잘 다지는 법 #2 - Matchers
Kotlin으로 ☕ Espresso UI 테스트 기반 잘 다지는 법 #2 - Matchers
2020.04.19 -
Swift로 🍏 UI XCTest 기반 잘 다지는 법 #2 - Action
Swift로 🍏 UI XCTest 기반 잘 다지는 법 #2 - Action
2020.04.04 -
Swift로 🍏 UI XCTest 기반 잘 다지는 법 #1 - 테스트 케이스
Swift로 🍏 UI XCTest 기반 잘 다지는 법 #1 - 테스트 케이스
2020.04.04 -
🥒 Cucumber 이해하고 잘 쓰는 방법
🥒 Cucumber 이해하고 잘 쓰는 방법
2020.04.03