๐ก view ํ ์คํธ(Recycler, Detail…)๋ฅผ ์งํํ๊ธฐ ์ํด์ AndroidJUnit4๋ฅผ ํ์ตํ์์ต๋๋ค!
AndroidJUnit4
- Android ๊ฐ๋ฐ ์ค ๋จ์ ํ ์คํธ๋ฅผ ์คํํ๊ธฐ ์ํ JUnit4 ํ ์คํธ ๋ฌ๋์ ๋๋ค.
- AndroidJUnit4๋ฅผ ์ฌ์ฉํด์ ์๋๋ก์ด๋ ํ๋ ์์ํฌ ๋ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ ์ฝ๋, ์ ํ๋ฆฌ์ผ์ด์
๊ตฌ์ฑ ์์์ ๋ํ ํ
์คํธ๋ฅผ ์คํํ ์ ์์ต๋๋ค.
- Activity, Service, ContentProvider..
- ํ ์คํธ ์ค์ Android API๋ฅผ ์ฌ์ฉํ ์ ์๋ค๋ ์ฅ์ ์ด ์์ต๋๋ค.
- ํ ์คํธ ํด๋์ค์ @RunWith(AndroidJUnit4::class) ์ด๋ ธํ ์ด์ ์ ์ถ๊ฐํ์ฌ AndroidJUnit4 ํ ์คํธ๋ฅผ ์ ์ฉํ ์ ์์ต๋๋ค.
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.example.myapp", appContext.packageName)
}
}
ํน์ง
- ํตํฉ ํ
์คํธ ์ง์
- Android ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฌ๋ฌ ๊ตฌ์ฑ ์์ ๊ฐ์ ์ํธ ์์ฉ์ ํ ์คํธ ํ ์ ์์ต๋๋ค.
- Instrumentation ํ
์คํธ
- ์ค์ ๊ธฐ๊ธฐ ๋๋ ์ ๋ฎฌ๋ ์ดํฐ์์ ์คํ๋๋ ํ ์คํธ๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
- Android API ์ฌ์ฉ
- ํ ์คํธ ์ค์ Android API๋ฅผ ํธ์ถํ ์ ์์ต๋๋ค.
ActivityScenarioRule
- AndroidX Test ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์ผ๋ถ๋ก, Activity ์๋ช ์ฃผ๊ธฐ๋ฅผ ์ฝ๊ฒ ์ ์ดํ๊ณ ํ ์คํธํ ์ ์๊ฒ ํด์ฃผ๋ ํด๋์ค์ ๋๋ค.
- ํ ์คํธ๊ฐ ์์๋ ๋ ์ง์ ๋ Activity๋ฅผ ์๋์ผ๋ก ์์ฑํ๊ณ ์์ํ๋ฉฐ, ํ ์คํธ๊ฐ ๋๋๋ฉด Activity๋ฅผ ์๋์ผ๋ก ์ข ๋ฃํฉ๋๋ค.
- ์ด๋ฅผ ํตํด ํ ์คํธ ์ฝ๋์์ ๋ช ์์ ์ผ๋ก Activity๋ฅผ ์์ฑํ๊ณ ์ ๋ฆฌํ ํ์๊ฐ ์์ด์ง๋๋ค.
- ๋ชจ๋ AndroidX Activity ๋ฐ Fragment ํ๋๊ณผ ํธํ๋๋ฉฐ, ์ํธ์์ฉํ ์ ์์ต๋๋ค.
์ ์ฉํ๊ธฐ
- ActivityScenarioRule์ ํ์ฉํ๊ธฐ ์ํด์ ์๋์ ๊ฐ์ ์์กด์ฑ์ ์ถ๊ฐํด์ผ ํฉ๋๋ค.
dependencies {
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test:rules:1.4.0'
androidTestImplementation 'androidx.test:core:1.4.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
- ์ดํ์ ํ ์คํธ ํด๋์ค์์ ActivityScenarioRule๋ฅผ ์ ์ํ๊ณ ์ฌ์ฉํ Activity๋ฅผ ์ง์ ํฉ๋๋ค.
@get:Rule
val activityRule = ActivityScenarioRule(MainActivity::class.java)
- ์ด๋ @get:Rule ์ด๋
ธํ
์ด์
์ ๊ผญ ์ ์ฉํด์ผ ํฉ๋๋ค.
- ์ด ์ด๋ ธํ ์ด์ ์ activityRule์ ํ ์คํธ ์์ ์ ์ ์คํํ๋๋ก ๋์์ค๋๋ค.
์ฃผ์ ๋ฉ์๋
- launch() : ์กํฐ๋นํฐ๋ฅผ ๋ช ์์ ์ผ๋ก ์์ํฉ๋๋ค.
- moveToState() : ์กํฐ๋นํฐ์ ๋ผ์ด๋ธ์ฌ์ดํด ์ํ๋ฅผ ๋ณ๊ฒฝํฉ๋๋ค.
- recreate() : ์กํฐ๋นํฐ๋ฅผ ์ฌ์์ฑ ํฉ๋๋ค.
- close() : ์กํฐ๋นํฐ๋ฅผ ์ข ๋ฃํฉ๋๋ค.
View ํ ์คํธ ์งํ
- ํ๋ก์ ํธ๋ฅผ ์งํํ๋ฉด์ Android ๋จ์ ํ ์คํธ๋ฅผ ํตํด์ View(Fragment)๋ฅผ ํ ์คํธํด๋ดค์ต๋๋ค.
ํ ์คํธ ํด๋์ค ์์ฑ
@RunWith(AndroidJUnit4::class)
class ProductDetailFragmentTest
- ํ ์คํธ ํด๋์ค๋ฅผ ์์ฑํ๊ณ , ์ด๋ ธํ ์ด์ ์ ์ ์ฉํด์ AndroidJUnit4 ํ ์คํธ์์ ๋ช ์ํฉ๋๋ค.
TestFixture & ActivityScenarioRule
- ์กํฐ๋นํฐ ์๋ช ์ฃผ๊ธฐ๋ฅผ ๋ฐ๋ฅด๋ ํ๋๊ทธ๋จผํธ๋ฅผ ์ ์ฉํ๊ธฐ ์ํด์ ActivityScenarioRule์ ์์ฑํฉ๋๋ค.
- ํ ์คํธ์ ํ์ํ ๋ฐ๋ณต ์์ (TestFixture)๋ฅผ ์ธํ ํฉ๋๋ค.
@get:Rule
val activityRule = ActivityScenarioRule(MainActivity::class.java)
@Before
fun setUp() {
val productDetailFragment =
ProductDetailFragment().apply {
arguments = ProductDetailFragment.createBundle(3L)
}
activityRule.scenario.onActivity { activity ->
activity.supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, productDetailFragment)
.commitNow()
}
}
- ํด๋น ์กํฐ๋นํฐ์์ ProductDetailFragment๋ก ์ด๋ํ์ฌ ํ ์คํธ๋ฅผ ์งํํ๋๋ก ํ์์ต๋๋ค.
ํ ์คํธ ์์ฑ
@Test
fun `์ ํํ_์ํ์_์ด๋ฆ์ด_๋ณด์ฌ์ ธ์ผ_ํ๋ค`() {
onView(withId(R.id.tv_detail_product_name)).check(matches(isDisplayed()))
}
@Test
fun `์ ํํ_์ํ์_๊ฐ๊ฒฉ์ด_๋ณด์ฌ์ ธ์ผ_ํ๋ค`() {
onView(withId(R.id.tv_detail_product_price)).check(matches(withText("0์")))
}
@Test
fun `์๋์_์ฆ๊ฐํ๋ฉด_์ ํํ_์๋์_๊ฐ์๊ฐ_๋ณด์ฌ์ ธ์ผ_ํ๋ค`() {
onView(withId(R.id.btn_add_count)).perform(click())
onView(withId(R.id.tv_count)).check(matches(withText("1")))
}
- ํ๋๊ทธ๋จผํธ์ ๋ํ ๊ฐ๋จํ ํ ์คํธ๋ฅผ ์งํํ์์ต๋๋ค.
- ๋ทฐ๊ฐ ์ ์์ ์ผ๋ก ๊ทธ๋ ค์ง๋์ง ํ์ธํ๊ธฐ ์ํ check() ๋ฉ์๋์ ํด๋ฆญ ์ ๋์์ ํ์ธํ๋ perform() ๋ฉ์๋๋ฅผ ํ์ฉํด์ ํ ์คํธ๋ฅผ ์งํํ์์ต๋๋ค.
์ฐธ๊ณ
https://developer.android.com/reference/androidx/test/runner/AndroidJUnit4
https://developer.android.com/training/testing/local-tests?hl=ko