효율적인 안드로이드 개발을 위해선 테스트에 대한 것을 빼놓을 수 없다. 안드로이드에서 UI Test 방법으로 Espresso, UI Automator 등이 있다.
UI 테스트를 위해선 어떤 프레임워크를 사용할 지 정해야 한다.
Espresso를 선택하였으니, build.gradle 파일에 해당 라이브러리가 추가되어 있는지 확인하고 없다면 추가한 후 테스트를 시작하면 된다.
defaultConfig {
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
dependencies {
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
androidTestImplementation 'androidx.test:runner:1.3.0'
}
이렇게하면 앱에서 Android Instrumentation Runner가 설정됩니다.
AndroidJUnitRunner 는 계측 실행기입니다. 이것은 본질적으로 전체 테스트 제품군을 실행하는 진입 점입니다. 테스트 환경, 테스트 APK를 제어하고 테스트 패키지에 정의 된 모든 테스트를 시작합니다.
프로젝트를 생성한 초기에 Unit, UI 테스트 디렉토리 내에 ExampleUnitTest, ExampleInstrumentedTest 파일이 있는 것을 확인할 수 있다.
UI 테스트를 위해서 새로운 테스트 Class를 생성하고 @RunWith (AndroidJUnit4 :: class)를 Annotation을 Class 위에 추가하면 기본적인 준비는 끝난다.
module-name/app/src/androidTest/java/.../
Espresso
에스프레소는 구글에서 제작하고 제공하는 안드로이드 UI를 자동으로 테스트하는 프레임워크
Espresso Annotation
@RunWith : 이 어노테이션으로 설정된 Class 러너를 결정하고, 초기화 한 다음 해당 Class에서 테스트를 실행하는데 사용된다. Android의 경우 AndroidJUnitRunner는 AndroidJUnit4 Class Runner가 구성 매개 변수를 전달할 수 있도록 설정되어 있는 명시적으로 확인한다.
@Before : @Test를 시작하기 전 사전에 진행해야할 정의에 해당된다. @Test가 시작되기 전 항상 호출되게 된다.
@After : 모든 테스트가 종료되면 호출된다. 메모리에서 resource를 release 할 수 있다.
@Test : @Before가 완료된 후 실제 테스트를 수행하도록 한다.
@Rule : 해당 Test class에서 사용하게 될 AcitivtyTestRule과 ServiceTestRule에 대하여 정의한다.
@BeforeClass, @AfterClass : 테스트 전, 후로 테스트 클래스에서 딱 한번씩만 수행된다.
@RequiresDevice : 에뮬레이터에선 테스트가 불가능하고, 오직 기기만 사용하도록 한다.
@SdkSupress : minSdkVersion을 지정할 수 있다.
@SmallTest, @MediumTest, @LargeTest : 테스트 성격을 구분하여 테스트 할 수 있다.
View Matchers
withId(), withText() 등 메서드가 존재한다.
onView(Matcher<View> viewMatcher) 메서드를 사용해 View 계층 구조에서 특정 뷰를 찾을 수 있다. 인수를 Matcher로 사용하지만 모든 UI 요소를 찾을 수 있도록 속성이 포함되어 있다.
withId() 메서드를 사용하여 id 값에 맞는 View를 찾아 사용할 수 있다.
View Actions
click(), typeText() 등 메서드가 존재한다.
View를 찾은 후 ViewAcitions를 사용하여 View에서 작업을 수행할 수 있다.
View Assertions
matches() 등 메서드가 존재한다.
View에서 작업을 수행한 후 View가 원하는대로 동작하는지 확인할 수 있다.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="Your Activity">
<EditText
android:id="@+id/edit_text"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/text_view"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" />
<TextView
android:id="@+id/text_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:text="hello world"
app:layout_constraintBottom_toTopOf="@+id/btn_view"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/edit_text" />
<Button
android:id="@+id/btn_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_view" />
</androidx.constraintlayout.widget.ConstraintLayout>
class MainActivity : AppCompatActivity() {
lateinit var editText : EditText
lateinit var textView : TextView
lateinit var button : Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
editText = findViewById<EditText>(R.id.edit_text)
textView = findViewById<TextView>(R.id.text_view)
button = findViewById<Button>(R.id.btn_view)
editText.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(p0: Editable?) {
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
textView.text = p0
}
})
button.setOnClickListener {
Toast.makeText(applicationContext, textView.text, Toast.LENGTH_SHORT).show()
}
}
}
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.*
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class EspressoTest {
@Rule @JvmField var activityTestRule = ActivityScenarioRule(MainActivity::class.java)
@Test
fun myTest(){
// edit_text라는 id를 갖고있는 View를 찾아 hello를 입력하고, keyboard를 닫는다.
onView(withId(R.id.edit_text)).perform(typeText("hello"), closeSoftKeyboard())
// text_view라는 id를 갖고있는 View를 찾아 현재 담긴 값이 hello인지 확인한다.
onView(withId(R.id.text_view)).check(matches(withText("hello")))
// btn_view라는 id를 갖고있는 Button을 클릭 이벤트를 발생시킨다.
onView(withId(R.id.btn_view))
.perform(click())
}
}
'Android' 카테고리의 다른 글
안드로이드 개념) CPU Profiler (0) | 2021.01.30 |
---|---|
안드로이드 개념) Debug, Debuging, Debugger (0) | 2021.01.30 |
안드로이드 개념) Complie, Build와 Gradle (0) | 2021.01.30 |
리소스와 코드의 연결 (0) | 2021.01.30 |
Android Test : Unit Test - Junit (0) | 2021.01.30 |