처음 학습하면서 작성한 글입니다. 필요시 추후 내용을 수정할 예정입니다.
틀린 부분이 있으면 언제든 지적해주면 감사하겠습니다 :)
Unit Test 에 대한 스터디를 진행하다, Espresso 를 사용하여 UI 테스트를 진행할 수 있다는 공식 문서를 확인할 수 있었다.
기존에 test 를 생각하고있지 않더라도 프로젝트를 생성하게 되면 gradle 에 junit 과 더불어 espresso 에 대한 implement 가 추가되어 있었는데, 이것이 테스트를 위한 것이라는 것을 이번에 처음 알게 되었다.
Espresso 사용법과 함께, 필자가 Espresso 를 적용하면서 잘 되지 않았던 부분을 해결한 방법에 대해서 포스팅하고자 한다.
우선,
Espresso 가 무엇인가?
간결하고, 신뢰할 수 있는 Android UI Test 를 작성할 수 있는 프레임 워크 이다.
Espresso 는 UI 상호작용을 실행할 때 작업을 동기화된 상태로 유지해 주며, 백그라운드 작업이 존재하는 경우 그 작업이 완료 된 뒤에 테스트를 진행한다.
즉, 안정적인 환경에서 UI Test 를 할 수 있게 도와주는 프레임워크라고 생각하면 된다.
그렇다면,
Espresso 를 사용하기 위한 설정을 진행해 보자.
Espresso 를 사용하기 위해 Gradle 에 선언해야하는 항목들이다.
해당 항목들은 Module 범위의 gradle 에 선언해야 하며, multiModule 구조라면 UI Test 를 진행 할 Module 에 해당 종속 항목을 선언해주어야 한다.
이 때, androidx 로만 선언을 해주어야 한다.
필자는 처음에 android developer 페이지가 아닌, vogella 에 작성되어있는 tutorials 를 보고 예제를 작성하였다.
해당 튜토리얼에서는 다음과 같이 종속 항목을 선언해 주었다.
위와 같은 방식으로 com.android.support 를 사용해 선언한 후, 뒤에 runner 와 rules 의 version 만 최신으로 변경하여 사용하였는데 결과적으로 문제가 발생하였다.
sync 하는 것에도 문제가 없으나, Test 코드를 작성하고 test 항목에 대하여 build 를 진행할 경우, Rule 어노테이션을 추가한 변수에서 제대로 context 를 가져오지 못하여 에러가 발생하게 된다.
이런 오류에 대하여 구글링을 좀 해보니,
AndroidX 를 사용할 때, com.android.support 와 동시에 사용하게 되면
충돌이 발생하여 정상 동작을 하지 않는 경우가 발생한다.
고 한다.
따라서, 동일하게 androidx를 사용하게 변경을 해주었더니 코드의 변경 없이 정상적으로 빌드가 되는 것을 확인할 수 있었다.
마찬가지로 Module 범위의 gradle 에서
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
라는 항목을 추가하여, 계측 테스트에 대한 실행 설정을 할 수 있다.
Espresso 사용을 위한 설정이 끝났으니,
실제로 UI Test 를 해볼 차례이다.
테스트 코드를 작성하기에 앞서 프로젝트를 만들면 항상 확인할 수 있는 2개의 테스트 패키지를 볼 수 있을 것이다.
이 두개의 패키지 중에서 사용할 클래스는 ExampleInstrumentedTest 클래스 이다.
이 두개의 테스트는 다음과 같은 차이를 가지고 있다.
Espresso 를 사용한 테스트는 계측 테스트로, 앞서 사용을 위한 설정을 할 때 계측 실행 테스트를 위한 설정을 한 것으로 이곳에서 작성한 테스트 코드를 실행시킬 수 있는 것이다.
테스트 코드를 하나씩 확인해 보자.
테스트에 사용되는 코드는 Junit4 의 어노테이션을 사용하여 선언한다.
@get:Rule 어노테이션을 달고있는 변수이다.
ActivityTestRule 을 사용하여 해당 작성할 Test 가 수행 될 Activity 에 대한 설정을 해준다.
해당 설정을 해주지 않으면, 수행 할 Acitivity 를 찾지 못하여 빌드 시 에러가 발생한다.
다른 예제를 확인해 보면, @Rule 로만 작성되어 있는 경우도 있는데,
최신 버전에서 사용하기 위해서는 @get:Rule 로 사용해주어야 한다.
현재 소스코드 상에서 ActivityTestRule 가 Deprecated 가 되어있다고 표시되어 있는데,
우선 Android Developers 에서도 ActivityTestRule 를 그대로 사용하고 있기 때문에 그대로 놔두었다.
@Before, @After 어노테이션을 사용하는 함수이다.
override 가 아니기 때문에 함수 명은 마음대로 지정할 수 있으며, 필자는 직접 사용하지는 않지만 테스트 시 많이 사용할 것으로 보여서 추가해 두었다.
@Before 어노테이션이 달린 함수의 경우, setUp 함수라고 생각하면 된다.
테스트가 실행되기 전에 수행되어야 하는 것들을 해당 함수에 포함시키면 된다.
@After 어노테이션이 달린 함수의 경우, Before 의 반대 역할을 한다고 생각하면 된다.
즉, 테스트가 모두 실행이 된 후에 종료되기 전에 수행되어야 하는 작업들을 해당 함수에 포함시키면 된다.
필자는 아주 간단한 text 입력과 click 이벤트만 확인하도록 예제를 구현했기 때문에 두 개의 함수에서 수행시킬 것이 없었지만, DB 작업이 필요한다던지 테스트 이전, 이후 작업이 필요한 경우에 필요한 작업을 구현하면 된다.
@Test 어노테이션을 추가하여 실제로 테스트를 진행 할 함수를 작성한다.
해당 어노테이션은 여러 개 작성할 수 있으며 각 테스트 어노테이션이 달린 함수마다 독립적으로 테스트를 수행시킬 수 있다.
이처럼 여러 개의 테스트 시나리오를 작성할 수 있으며, 선언된 두 개 이상의 테스트를 한번에 수행시키는 작업도 가능하다.
테스트를 수행시키는 함수는 다음과 같은 형식으로 작성할 수 있다.
onView(ViewMatcher)
.perform(ViewAction)
.check(ViewAssertion)
여기서, 필자는 check 함수를 사용하지 않고 동작만 수행하도록 하였다.
check 부분에서는 perform 동작 후 조건을 추가하여 체크하는 부분인데 이 부분의 사용까지는 필요 없다고 생각했기 때문이다.
onView(ViewMatcher) 에는 동작을 수행할 리소스의 id 값을 넣어주면 된다.
작성한 예시를 확인해 보면, onView(withId(R.id.text1)) 과 같이 선언해 주었다. withId(R.id.text1) 를 사용하여 이벤트를 수행할 ID 값을 작성하면 된다.
perform(ViewAction) 에는 말 그대로 수행할 동작에 대해 작성해 주면 된다.
ViewActions 에서 선언할 수 있는 다양한 동작들이 존재하는데, 필자는 텍스트를 입력하고, 지우고, 키보드를 내리고, 버튼을 클릭하는 이벤트를 추가하였다.
이처럼 다양한 동작을 지정해 줄 수 있으니 필요한 동작을 확인하여 사용하면 된다.
필자가 작성한 예시에 보면 주석을 추가해 둔 부분을 확인 할 수 있다.
perform 으로 선언할 때, 한가지의 동작만 수행하는 것이 아니라 , 로 구분하여 순차적으로 여러 개의 동작을 수행하게 작성할 수 있다.
전체 소스코드는 다음과 같다.
테스트 코드까지 작성을 끝냈으면,
테스트를 진행시켜 볼 차례이다.
(필자는 테스트 코드를 작성하고, 어떻게 테스트를 진행시키는지 한참 찾았다.)
우선,
Test 어노테이션을 작성한 테스트 시나리오 함수를 확인해 보자.
함수 좌측에 보면 버튼이 하나 생겨있을 것이고, 그것을 클릭하게 되면 다음과 같이 나오게 된다.
Run 을 누르게 되면 현재 연결된 디바이스 혹은 VM 을 통해 작성한 테스트 시나리오대로 동작을 수행 한 후, 마지막 동작을 수행 후에는 앱이 자동으로 종료되게 된다.
위와 같은 방법으로 최초 1회 실행을 시키거나, Gradle sync 를 맞추게 되면 상단에
다음과 같이 Test 함수를 Run 시킬 수 있도록 항목이 뜨게 된다.
필자는 Gradle sync 를 맞추는 작업을 수행해도 위의 부분에 test 함수가 나오지 않아서 함수 좌측에 있는 버튼을 통해 테스트 함수를 실행 시켰다.
여기서,
실행시킬 수 있는 목록을 보면 각 테스트 항목 뿐 아니라, 테스트 항목들을 작성해 놓은 TestClass 또한 UI Test 로 실행시킬 수 있게 되어있다.
해당 항목으로 Test 를 실행시키게 되는 경우, class 내부에 선언해 둔 모든 Test 어노테이션이 붙은 함수를 수행시키게 된다.
하지만 이 때, 순차적으로 테스트가 진행 되는 것이 아니라 상단 툴바에 표시된 테스트 함수의 역순으로 실행이 되며,
각 테스트 함수가 실행 될 때 앱이 켜지고, 함수가 끝나면 앱이 종료되면서 독립적으로 수행이 된다.
기본적인 것들만 사용하여 Espresso 를 사용한 UI Test 를 진행해 보았는데, 기본적인 것들만 사용하니 상당히 구현하기도 쉽고 재미가 있는것 같다.
UI 테스트만 진행한다면, 이정도의 클릭 이벤트, 텍스트 입력 및 제거 이벤트 수준만 사용해도 많은 부분의 ui 테스트를 진행할 수 있을 것 같다고 생각한다.
하지만, 실제 프로젝트에서 좀 더 복잡도가 높은 테스트 시나리오를 작성하게 된다면 심화된 버전으로 Espresso 를 사용해야 하지 않을까 싶다.
추후 모듈 아키텍처 구조에도 UI Test 를 적용시켜 봐야겠다. 모듈이 나누어진 구조에서 어떻게 적용시켜야 할지, 다른 모듈에 있는 test 시나리오를 사용할 수 있는지 확인이 필요해보인다.
필자가 예제에 사용한 Test code 는 상당히 간단한 작업에 대한 것들만 사용하였으므로, 좀 더 자세한 사용 방법에 대해서는 Developer 공식 문서에서 확인하기를 바란다.
https://developer.android.com/training/testing/espresso/basics
해당 게시글에 사용 된 예제 코드는 Github에 업로드 해두었다.
https://github.com/HeeGyeong/UnitTestSample
'Android > TDD' 카테고리의 다른 글
[TDD] TDD 란 ? (0) | 2022.02.27 |
---|---|
[Mockito] Mockito를 사용해 Instrumented Unit Test를 해보자 (0) | 2022.02.25 |
[Mockito] Mockito를 사용하여 Unit Test 를 해보자 (feat. Truth + Junit4) (2) | 2022.02.24 |
[Truth] Truth를 사용하여 Unit Test 를 해보자 (feat. Junit4) (0) | 2022.02.23 |
[Mockito] Mock 객체 란? (0) | 2020.06.10 |