Android/Android Test

UI Test (with Espresso)

728x90
반응형

Espresso란?

 

1. UI  테스팅 기초

 

gradel 설정

android {
    defaultConfig {
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" // 기존에 있음
    }
}

dependencies {
    androidTestImplementation 'androidx.test:rules:1.2.0'
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'// 기존에 있음
    androidTestImplementation 'androidx.test.espresso:espresso-accessibility:3.3.0-alpha05' 
}

 

 

 

 

Test code 작성

mainActivity

class MainActivity : AppCompatActivity() {
    val count = ObservableInt(0)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        val binding: ActivityMainBinding = DataBindingUtil.setContentView(
            this,
            R.layout.activity_main
        )

        binding.count = count

        binding.addButton.setOnClickListener {
            count.set(count.get() + 1)
        }

        binding.subtractButton.setOnClickListener {
            count.set(count.get() - 1)
        }
    }
}

 

 

TestCode

@RunWith(AndroidJUnit4::class)
class CounterInstrumentedTest {

    @Rule
    @JvmField
    var mActivityTestRule: ActivityTestRule<MainActivity> = ActivityTestRule(MainActivity::class.java)

    @Test
    fun testIncrement() {

        Espresso.onView(withId(R.id.add_button))    // 1) 뷰 계층 구조에서 하나 이상의 뷰를 찾습니다 (추가 버튼).
            .perform(ViewActions.click())   // 2) 어떤 방식 으로든 view들과 상호 작용합니다 (클릭 수행).

        Espresso.onView(withId(R.id.countTV))
            .check(matches(withText("1")))
    }
    // 3) UI의 상태에 대한 assertion을 작성하십시오 (업데이트 된 개수가 표시됨을 assert 함).
    companion object {
        @BeforeClass
        @JvmStatic
        fun enableAccessibilityChecks() {
            // TODO: uncomment to enable accessibility checks.
//             AccessibilityChecks.enable()
        }
    }
}

 

 

1) 뷰 계층 구조에서 하나 이상의 뷰를 찾습니다(추가 버튼).

2) 어떤 방식 으로든 view들과 상호 작용합니다(클릭 수행).

3) UI의 상태에 대한 assertion을 작성하십시오 (업데이트 된 개수가 표시됨을 assert ).

 

2.  앱의 접근성을 평가

 접근성 테스트 프레임 워크 ( ATF )를 통해 앱의 접근성을 평가한다. 

 

 


Espresso의 메인 컴포넌트

1. Espresso

  • onView() 와 onData() 를 통해 뷰와 상호작용 할 수 있는 엔트리 포인트입니다.
  • 뷰에 대한 접근이 필수는 아니며, pressBack() 같은 메소드도 있습니다.

 

onView, check, perform

에스프레소에서 사용하는 기본 함수들로,

 

onView : 대상 view 찾고

- id와 텍스트로 찾는 방법이 있음

.onView(withId(R.id.button)) // 에서 id 가 button 인 뷰를 화면에서 찾는다
.onView(withText("Welcome")) // "Welcome" 이란 텍스트를 가진 뷰를 찾는다

check : view의 상태를 확인

찾기만 할 경우 못찾아도 아무런 에러가 발생하지 않기에 '꼭' check 함수로 확인을 해주어야 함

.check(matches(isDisplayed())) 
.check(matches(withText("Welcome"))) 
.check(matches(allOf(isDisplayed(),withText("Welcome"))))

perform : view에게 특정 동작시킴

- 해당 뷰가 할 수 있는 동작이 아닐 경우는 실행 시점에서 에러가 발생

.perform(click()) 
.perform(click(), longClick())

2. ViewMatcher : Find a View

  • Matcher<? super View> 인터페이스를 구현한 클래스들의 집합입니다.
  • onView()의 파라미터로 사용되며, 예시로 withId() 등이 있습니다.
  • ViewMatchers는 뷰를 찾기 위해서도 ( onView ) 쓰고, 뷰의 상태를 확인하기 ( check ) 위해서도 사용한다.
  • 대다수의 경우에 onView() 메소드는 하나의 View에 매칭되는 hamcrest matcher를 취합니다.
onView(withText("Welcome")) 
		.check(matches(allOf(isDisplayed(),withText("Welcome"))))

// isDisplayed()
onView(isDisplayed())
    
    
// withId()
onView(withId(R.string.button))



// withText()
onView(withText("Button")) 

// withText()
onView(withText(R.string.button))

// withText()
onView(withText(object : CustomMatcher<String>("has multiple 't'"){ 
	override fun matches(item: Any?): Boolean { 
		return (item as? String)?.let{ 
			it.filter { it == 't' }.count() == 2 
		}?: false 
	} 
}))

 

그외..

hasContentDescription, withContentDescription

withParent, withChild, hasSibling, hasDescendant

 isAssignableFrom

 

isClickable, isEnabled, isSelected, withClassName

 

3. ViewActions : Perform an action on a view

  • ViewAction 인터페이스를 구현한 클래스들의 집합입니다.
  • ViewInteraction.perform() 메소드의 파라미터로 쓰이며, 예시로 click() 등이 있습니다.

 

클릭 동작

// 클릭 -> 롱 클릭 -> 더블 클릭이 순차적으로 일어난다.
onView(withId(R.id.button))
	.perform(click(), longClick()) 
	.perform(doubleClick())

 

텍스트 타이핑 동작

 
onView(withId(R.id.editText))
	.perform(clearText())		// clearText()텍스트를 지우는 동작을 하고,
	.perform(typeText("Hello"))		// typeText()타이핑하는 동작
	.perform(typeTextIntoFocusedView("World"))		// typeTextIntoFocusedView()타이핑하는 동작


onView(withId(R.id.editText))
	.perform(replaceText("Hello World")) //텍스트를 대체 >> Hello World
	.perform(typeTextIntoFocusedView("!!")) // 맨 앞에 글자를 쓰기 시작 >> !!Hello World

 

 

스크롤(scroll)

onView(withId(R.id.editText))
	.perform(scrollTo(), click())

 

스와이프(swipe)

perform(swipeUp(), swipeDown(), swipeRight(), swipeLeft())

 

불변해야하는 뷰 처리

val viewAssertion = matches(withId(R.id.button)) 
addGlobalAssertion("button always isDisplayed",viewAssertion) 
removeGlobalAssertion(viewAssertion)

 

반복 행동 처리

repeatedlyUntil

onView(withId(R.id.button))
	.perform(repeatedlyUntil(click(), hasSibling(withText("Welcome")), 10))

 

키보드 처리

closeSoftKeyboard, pressImeActionButton, pressKey, pressBack

 

 

링크 관련 처리

openLink, openLinkWithUri

 

 

4. ViewAssertions : Perform an action on a view

  • ViewAssertion 인터페이스를 구현한 클래스들의 집합입니다.
  • ViewInteraction.check() 메소드의 파라미터로 쓰이며, 예시로 matches() 등이 있습니다.
// matches()
onView(withId(R.id.textView))
	.check(matches(not(withText("Hello World!"))))


onView(withText("Barabarabarabam"))
	.check(doesNotExist())
    
    
onView(withId(R.id.linearLayout))
	.check(
		selectedDescendantsMatch(
			isAssignableFrom(TextView::class.java), 
		hasContentDescription() 
		)
	)

 

 

 

 

 

 

 

 

 

 

 

 

공식 문서

 

 

Samples Code

참고

two22.tistory.com/9

 

 

 

 

 

728x90
반응형