Android에서 Appium을 이용한 UI Automation
User Custom Canvas 혹은 SurfaceView를 이용한 Application을 개발한 경우, 자동화 테스트를 위해 OpenCV를 많이 이용한다. (이미지를 비교하여 일정 이상의 확률 Threshold → Element를 찾고, 이를 이용하여 테스트하는 방법) SurfaceView를 이용하는 게임이나 혹은 NativeUI를 직접 그리는 Application의 경우가 그렇다.
반면 Android Native UI를 이용한 APP의 경우 Appium을 이용하여 자동화 테스트를 진행하기 위해 특별히 추가해야할 작업은 없다. Android에서는 Native UI를 사용하거나 Webview를 이용한 Hybrid App인 경우, 접근성(Android Accessibility) 을 통해 UI Automation 기능을 제공하기 때문이다. 이 경우 UI AutomatorViewer나 Appium Inspector 를 이용하여 해당 Element의 ID를 얻고 간단한 Tab, Sendkeys, Clear 등을 테스트해 볼 수 있다.
User Custom Canvas를 이용해 직접 화면을 그리는 경우 Appium을 사용하려면
User Custom Canvas를 사용한 경우, 즉 화면을 Skia나 OpenGL등 Graphic Engine을 이용해 FrameBuffer(Canvas)에 직접 그리는 경우에는 테스트 하고자 하는 Element(정확히 접근성 Accessibility Node) 에 대한 정보를 직접 안드로이드 접근성 기능을 통해 알려줘야 한다.
즉 Appium Test Framework을 이용하여 자동화 테스트를 하고자 한다면, 추가적으로 구현해야 할 것들이 있다.
ExploreByTouchHelper를 이용하는 방법은 하기 링크 참고
https://developer.android.com/reference/androidx/customview/widget/ExploreByTouchHelper
ExploreByTouchHelper를 구현한 샘플 소스는 하기 링크 참고
Host View, Virtual View 구성하는 법
안드로이드에서는 화면의 기본 구성 단위는 View인데, SurfaceView에서는 더이상 하위 View정보를 제공하지 않기 때문에(제공할 수 없기 때문에) 자동화 테스트를 위해 ExploreByTouchHelper를 구현하고 이를 아래와 같이 설정한다.
private MyExploreByTouchHelper mExploreByTouchHelper;
ViewCompat.setAccessibilityDelegate(this, mExploreByTouchHelper);
이렇게 하면 해당 View의 하위에는 ExploreByTouchHelper에 의해 View Tree 혹은 VirtualView의 Tree가 구성되며, Tree를 구성하기 위한 정보를 요청한다.
이때 이 View를 Host View라고 하고, 이 Host View는 Virtual View Tree를 포함한다.
Tree를 구성하기 위해 Accessibility에서 해당 정보를 요청하는데, VirtualViewId를 인자로 하고, 아래 메써드를 호출한다.
@Override public AccessibilityNodeInfoCompat createAccessibilityNodeInfo(int virtualViewId)
View.NO_ID이면 Host 즉, 해당 Host View 정보를 요청하는 것으로 (예를 들어) SurfaceView의 크기, Visible, Class Name등을 Host의 node에 담아준다.
아울러 노드 트리의 Root 정보를 호출하기 위해 아래와 같이 addChild 메써드를 추가한다.
node.addChild(mHost, ROOT_ID);
이를 통해 Root 정보를 요청하는 createAccessibilityNodeInfo
메써드가 호출되게 된다. 여기서 부터 본격적인 Tree 정보를 요청하게 된다. Root에서 1차 자식들 정보를 addChild 하면, 자식들 VirtualView를 파라미터로 하는 createAccessibilityNodeInfo
가 연쇄적으로 호출되게 된다. 즉 Tree Traversal과 동시에 Virtual View Tree가 형성이 되는 것이다.
안드로이드 공식 문서에는 나와있지 않지만, Depth First Search를 한다.
@Override public AccessibilityNodeInfoCompat createAccessibilityNodeInfo(int virtualViewId) {
if (virtualViewId == View.NO_ID)
{
return getNodeForHost();
}
else if (virtualViewId == ROOT_ID)
{
return getNodeForRoot();
} else
return getNodeForVirtualViewId(virtualViewId);
}
현재 node에 하위 자식node를 추가하는 코드
ArrayList<TreeNode> childNodes = currentNode.getChildNodeArray();
for (TreeNode childNode : childNodes) {
node.addChild(mHost, childNode.getVirtualViewId());
}
Shadow Tree
Virtual View Tree는 안드로이드 접근성 기능에서 사용하는 것으로, 이 Tree를 구성해 주기 위해서는 이미 Canvas를 그리기 위한 Tree, 혹은 Tree 정보를 알고 있어야 한다. 보통은 SceneGraph의 정보를 이용하는 경우가 많은데, 이와 관련된 사항은 컴퓨터 그래픽스 관련 내용을 따로 찾아보기 바란다.
ROOT_ID 와 일반적인 virtualViewId인 경우, 하위 Tree의 자식 정보들 알고 있어야 한다. 즉 해당 Tree 정보를 미리 가지고 있어야 한다는 뜻이다. 이를 위해 Host View나 Root 정보를 요청할 때 트리를 구성하거나 트리 정보를 미리 가져와야 한다. 필자는 이를 ShadowTree라고 부른다.
public class TreeNode {
private String nodeName;
private int virtualViewId;
private TreeNode parentNode;
private ArrayList<TreeNode> childNodeArray = new ArrayList<>();
실제 SceneGraph는 자체 Thread와 Message-Q를 이용해 구현하는 경우가 많은데, 해당 Tree에 접근하여 Tree 정보를 가져오기 위해서는 Semaphore/Mutex Lock을 걸어야 하고, 이것은 그래픽 랜더링에 치명적인 속도 저하를 가져온다.
그래서 이를 해결하기 위해 SceneGraph상에서 새로운 Node(혹은 Object/Element)가 생성되면, 이를 Event로 받아 Application(Java/Kotlin)내부에 새로운(그러나 아주 기본적인 정보만을 포함한) Tree로 구성해 놓는다. (정확히는 HashMap으로 구성해 놓고, UIAutomator가 요청할 경우 이를 Tree로 만든다 ) 그리고 이 Tree를 이용하여 안드로이드 접근성에 Virtual View Tree 정보를 제공하는 방법이다.
if (eventType == Constant.TYPE_ACC_NODE_CREATE) {
sendNotifyFlag = true;
addNewNodeToVirtualViewMap(jsonAccessibilityInfo, eventType);
} else if (eventType == Constant.TYPE_ACC_NODE_DESTROY) {
sendNotifyFlag = true; deleteNodeFromVirtualViewMap(jsonAccessibilityInfo);
}
Appium에서 제공하는 간단한 Click테스트를 위해 Accessibility Node 정보에는 최소한 Node ID와 Rect 정보가 포함되어야 한다. Node Id는 Appium Inspector와 UIAutomatorViewer를 참고하여 만들면 된다. 아울러 Node의 Rect 정보는 해당 화면에서의 위치를 가져오면된다. 동적으로 생성되거나 위치가 수시로 변하는 경우를 대비해 Rect정보를 제공하는 메써드는 미리 구현해 놓는것이 일반적이다.
ExploreByTouchHelper를 구현한 샘플 소스는 하기 링크 참고
'나의 SW개발 이야기 > 안드로이드' 카테고리의 다른 글
[Android] Virtual Reality 개발 - 기본 동작 방식 (0) | 2021.03.03 |
---|---|
Android Minimum API 별 버전 빌드하기 (0) | 2019.04.05 |
AndroidX Runtime Exception on Android 4.4.2 (0) | 2019.03.08 |
V8 안드로이드 버전 빌드 (0) | 2017.03.27 |
Uploaded by Notion2Tistory v1.1.0