Cordova – hybrid app 만들기 위한 프레임워크

  • CORDOVA
    • Apache 재단에서 지원중인 하이브리드 앱 제작을 위한 프레임워크다.
    • https://cordova.apache.org/docs/en/6.x/guide/overview/index.html  참조
    • Cordova 프로젝트를 기반으로 진행되고 있는 프로젝트들
      • PhoneGap : 폰갭은 코르도바를 폰갭 콘트롤러로 패키징했는데, 기본 코르도바 플러그인들과 추가적인 폰갭 플로그인들을 이용해서 모바일 디바이스용 웹 페이지를 만들어 하이브리드 앱을 개발할 수 있는 프레임워크이다.
        링크된 페이지에서 설치부터 앱 작성, 빌드하는 과정 등이 상세히 설명되어 있음.
      • Ionic : Angular.js를 사용하여 모바일 디바이스용 웹 페이지를 좀 더 쉽게 만들 수 있게 해준다는 점이 코르도바나 PhoneGap과 다름
      • Monaca : PhoneGap/Coredova와 함께 html5 하이브리드 앱을 쉽게 개발할 수 있게 해줌
    • 사전 설치
      • node 설치
      • cordova cli 설치
        • npm install -g cordova
        • 업데이트
          • npm update -g cordova
      • 앱 템플릿 생성
        • cordova platform add android –save
        • cd hello
      • add platforms( 플랫폼 데이타를 추가해 준다)
        • cordova platform add ios –save
        • cordova platform add android –save
        • cordova platform ls : 현재 set이 어떤 플랫폼에 대한 셋인지 체크한다.
        • 플랫폼 업데이트
          • cordova platform update android –save
          • cordova platform update ios –save
      •  앱을 빌드
        • 빌드하기 위해서 SDK가 설치되어 있어야 함
        • SDK 설치되었는지 확인하는 방법
          • cordova requirements
        • cordova build android
      • 에뮬레이터 실행
        • cordova emulate android
        • 혹은 원하는 에뮬레이터 실행해 놓는다.
      • 앱 실행
        • cordova run android
      • IDE(Xcode, android, eclipse 등으로 앱을 불러와서 www 폴더 아래에서 앱을 수정할 수 있다)
      • 플러그인 추가
        • 설치할 플러그인 검색
          • cordova plugin search camera
            browser가 열리며 camera를 이름에 포함하는 모든 plugin을 표시해 준다. 각 plugin은 이름과 함께 어떤 플랫폼에서 지원되는지 나타내준다.
        • 플러그인 설치
          • cordova plugin add cordova-plugin-camera
            플러그인을 설치하면 app/platforms/android/res/xml/config.xml에 설치된 플러그인에 대한 feature 를 추가해 준다.
        • 플러그인 제거
          • cordova plugin remove cordova-plugin-camera
        • 설치된 플러그인 검색
          • cordova plugin ls
      • plugin 소개
      • 그 밖에 디버깅 툴 소개
        • weinre : WebView를 위한 리모트 디버깅이 지원되지 않는 경우 리모트에서 디버깅해줄 수 있도록 한다.
      • Core Plugin API 사용
      • app을 위한 템플릿 사용하기
        • npm package
          • cordova create hello com.example.hello HelloWorld –template <npm-package-name>

        • git repository
          • cordova create hello com.example.hello HelloWorld –template <git-remote-url>

        • local
          • cordova create hello com.example.hello HelloWorld –template <path-to-template>

      • 데이터 저장
      • security guide
  • 테스트 샘플
광고

Android – constraintlayout, hybrid app(browser type)

  • constraintlayout
    • 안드로이드 스튜디오가 버전 2.3이 되면서 Activity의 기본 layout으로 설정됨
      (API 9(Android 2.3)이상 Android Studio 2.3이상에서만 동작함)
    • 화면 구성요소간 구성요소와 화면간의 제약사항을 정해 layout을 정하게 한다.(iOS의 autolayout와 유사하다)
    • 안드로이드 스튜디오에서 제공하는 UI로 조정하기 어렵다. 차라리 코드로 치는게 난 듯하다.
    • 안드로이드 스튜디오에서 제공하는 UI를 사용하여
      • 구성 요소들을 여러개 선택하여 선택된 구성요소간의 정렬을 지정할 수도 있고
      • 구성 요소를 선택하면 선택된 구성요소 사면의 각면 가운데에 점이 생기는데 그 점과 다른 구성요소를 연결하여 구성요소간 간격 속성을 설정할 수도 있다.
  • hybrid app
    • 웹앱과 네이티브 앱이 합쳐진 형태이다. 네이티브 앱은 각 OS에서 제공하는 SDK를 가지고 만든 앱을 말한다.  웹앱의 형태로 만들어져서 네이티브 앱처럼 설치형 앱으로 사용된다.
    • 각 OS별로 WebView 기능을 지원하고 있어 html, css, javascript등을 이용한 코드는 WebView를 통해 동작하게 하고 네이티브 기능을 함께 사용하도록 하고 앱으로 빌드하기 위한 frameworkwork이 필요하다.
      • 폰갭(코르도바)은 각 OS에 대한 네이티브 기능을 사용하도록 한 것으로 WebView와 Native 코드가 통신하는 방법을 정의한다. 각 OS별로 통신 규격이 다르다. (라이브러리 형태로, 이클립스나 개발 툴을 통해 라이브러리 다운로드 받아 사용)
      • 앱스프레소 또한 멀티 디바이스(iOS, Android)의 hybrid app 을 개발할 수 있도록 하는 개발 프레임워크이다. 앱스프레소는 UI 템플릿을 제공한다. 이 프레임워크에는 Sencha Touch, JQuery Touch, JO 등의 템플릿을 제공하고 있다. 웹 앱을 개발하다가 네이티브 코드를 호출하여 사용하는 방식이다. (에디터 형태로 제공됨, sencha touch 등을 위한 라이브러리는 따로 받아야 함)
      • 앱셀러레이터 티타늄, 리액트 네이티브 : JavaScript로 Native 모바일 앱을 만드는 크로스 플랫폼 프레임워크

iOS 1 – tabview, autolayout

  • autolayout
    • UI 요소의 크기와 위치가 아닌 그것에 대한 제약사항을 넣어서 파편화 문제를 해결하려 함
    • constraint 추가 방법
      • align : UI 요소의 전체 뷰에 대한 align(가로 중간, 세로 중간) 제약 사항이나 다른 UI요소와의 정렬에 대한 제약 사항 정의
      • pin : UI 요소의 전체 뷰에 대한 공간적 길이(상단 x, 왼쪽 y, 위 z, 아래 w) 제약 사항이나 다른 UI 요소와의 공간적 길이에 대한 제약 사항 정의
    • 사용 방법
      • 제약 설정하려는 기준이 되는 UI요소인 경우, 설정하려는 UI요소 클릭하여 선택하고 View 아래 오른쪽 화면에 Align이나 Add New Constraints 아이콘 클릭하여 데이타 입력
      • 첫번째 요소가 아닌 경우, 제약 설정하려는 기준이 되는 UI 요소 클릭하고 키보드의 Ctrl 버튼 누르고 기준을 삼은 UI요소 쪽으로 드래그 하여 이동하면 기준을 삼은 UI요소가 회색으로 선택되면 드래그와 Ctrl 버튼 클릭 해제한다. 그러면 팝업이 떠서 constraint 설정할 수 있게 나옴(기준이 되는 UI는 전체 뷰가 될 수도 있음)
    • 예제 작성
      • Tab을 세 개 가짐 => tabbed application + 1 View Controller
      • 각 탭마다 이미지와 이미지 아래 이름과 이미지에 대한 정보가 표시됨
      • 각 탭 별로 이미지를 기준, 이미지는 수평 가운데 위치, 이미지 아래 8 pixel 아래 이름 텍스트 표시, 그 아래 8 pixel 아래 정보 텍스트 표시
    • App github

Appium 사용기 – IOS swift app UI 테스트

Android 앱에 대한 Appium 사용기는 많으나 iOS에 대한 내용이 없는 거 같아 사용기를 적어 보았다

Appium 개요 및 배경

Appium은 모바일 애플리케이션의 테스트 자동화를 가능하게 해주는 오픈소스 프레임워크이다. native, hybrid, mobile web 등 모든 타입의 모바일 애플리케이션의 테스트 자동화를 가능하게 해준다. 자동화된 테스트는 실제 디바이스나 에뮬레이터, 혹은 시뮬레이터에서 실행될 수 있다.

오늘날 대부분의 모바일 앱은 iOS, Android 두 가지 플랫폼 기반으로 작성되고 있다. 같은 기능의 앱일지라도 서로 다른 플랫폼을 기반으로 작성되고 실행되어야 하기 때문에 유지보수 비용이 두 배로 증가한다. 이러한 환경에서 Appium은 하나의 테스트 코드로 서로 다른 플랫폼(Android, iOS, Windows, FirefoxOS)의 앱을 테스트할 수 있도록 하여 제품 유지보수의 비용을 줄여준다.

이것이 가능한 이유는 아래 그림에서 볼 수 있듯이 Appium의 소프트웨어 구조가 서로 다른 플랫폼 벤더가 제공하는 테스트 프레임웍(UIAutomation, XCUITest for OSX, UIAutomator, Selendroid for Android, WinAppDriver for Windows, Marionette for Firefox OS)을 공통의 WebDriver API(Selenium WebDriver API)로 랩핑을 하고 있기때문이다. 이러한 구조적 특성으로 인해 유사한 역할을 하는 Calabash와는 다르게 앱을 수정하거나 재컴파일할 일이 없다는 것도 주목할 만한 점이다.

appium_supported_platform[그림 1 Appium 의 테스트 기반 프레임워크]

Appium Architecture

Appium의 아키텍쳐이다.

appium architecture

1) JSON wire protocol로 automation command를 보낸다.
2) vendor 디펜던트한 방식으로 automation command를 호출시킨다.
3) 실행 결과를 appium 서버로 전송한다.
4) 실행 결과를 콘솔에 로깅한다.

Appium은 벤더가 제공한 테스트 프레임워크를 Selenium WebDriver API로 랩핑한 client library들을 다양한 개발 언어로 개발자에게 제공한다.

개발자는 기호에 맞는 언어와 테스트 프레임워크를 선택하여 라이브러리가 제공하는 API(WebDriver JSON Wire Protocol)를 사용하여 자동화된 테스트 스크립트를 작성한다.

Appium 서버는 node.js로 작성되었으며 WebDriver 세션을 생성하고 관리하는 HTTP 서버 이다. 스크립트 상의 명령어들을 각 벤더의 메카니즘에 따라 실행하고 디바이스에서의 실행 결과를 받아와 콘솔에 기록한다.

아래의 그림은 iOS에서의 Appium 아키텍쳐 그림이다.

ios appium

Appium Server와 디바이스 사이에서 iOS가 제공하는 instruments 환경을 사용하여 디바이스에서 명령어를 실행한다.

아키텍쳐 그림을 통해 테스트 라이프사이클을 알 수 있다.

  1. Client(스크립트의 API)은 WebDriver JSON Wire Protocol을 사용하여 Appium서버와 통신한다.
    Appium Server는 client 요청을 받아 client와의 automation session을 생성한다. 그리고 클라이언트가 원하는 기능(capabilities)을 확인한 후 vendor가 제공하는 UIAutomation(iOS 9.3 이하)이나 XCUITest(iOS 9.3 이상) 으로 client와 연결 시킨다.
  1. 이후 client의 요청은 연결된 UIAutomation나 XCUITest 를 통해 device/simulator내에서 실행되고 있는 js로 전달된다.
  2. js는 AUT(Application Under Test) 상에서 클라이언트가 요청한 action이 실행되도록 한다.

본 문서는 iOS의 사용기이니 다른 플랫폼의 세부 라이프사이클은 생략하기로 한다.

테스트 자동화 개발환경 구성

Appium을 사용해 iOS 앱의 테스트 자동화를 실행하기 위해서는 iOS 앱 실행 환경과 Appium 서버 실행 환경, 테스트 스크립트 작성 및 실행 환경이 필요하다.

iOS 앱 실행 환경 구성하기

아래에서는 OSX 에서 iOS Application을 검증하기 위해 필요한 환경 설정 방법을 정리한 것이다. 원문은 https://github.com/appium/appium/blob/master/docs/en/appium-setup/running-on-osx.md#authorizing-ios-on-the-computer 참조하기 바란다.

  • MAC OS X 10.10 이상이 설치될 것을 권고한다.
  • Xcode와 iOS SDK(s)가 설치되어야 한다.
    • Xcode를 설치하면 기본적으로 iOS SDK가 설치된다.  Xcode 7.1 이상은 풀 테스팅을 지원하고, 이전 버전은 제한된 테스팅을 지원한다.
  • iOS Simulator 사용에 대한 인증이 필요하다
    • 아래 그림에서와 같이 npm으로 authorize-ios을 설치한다.
      install_ios_authorize

      • 실행방법은 두가지가 있다.
        Command line으로 실행시, sudo authorize_ios, Appium.app 실행시, GUI로 인증 가능하다.
        그림은  Command line을 통해 실행한 결과이다.
        authorizeios
  • Xcode 7.x 이상(iOS >-9.0) 사용시, instruments 의 delay이슈가 있다. 이는 테스트 속도를 느리게 한다. 이를 해결하기 위해 appium은 IWD(instruments without delay)를 제공한다.

sh /bin/xcode-iwd.sh

실행 예제는 다음과 같다.
sh ./bin/xcode-iwd.sh /Applications/Xcode.app /Users/xyz/appium-instruments/

  • Xcode 6 사용 시, 아래 사항을 주의한다.
    • sendKeys기능을 사용하기 원하는 경우, simulator를 먼저 띄우고 soft keyboard 사용되도록 디폴트 설정을 바꿔야 한다.
    • Xcode Feature중 Devices를 가지는 경우, device list에 중복되는 device 이름을 가지지 않도록 해야한다.
  • iOS8 사용 시, 아래 사항을 주의한다.
    • UIAutomation으로 테스트 시. 아래 그림에서와 같이 device/emulator의 device setting에서 UIAutomation을 반드시 enable시킨 후 테스트해야 한다.
      setting
    • XCUITest(iOS 10.0, Xcode 8.0이상)으로 테스트 시, 아래 그림과 같이 Carthage를 설치해야 한다.
      xcuitest_install
    • Xcode 8.x 이상(iOS >10.0) 사용시, Appium GUI 버전 App은6이상을 사용해야 한다. 현재 1.6은 Inspector를 사용할 수가 없다. 테스트 케이스를 만들기 위해, 화면을 구성하는 Element들의 reference를 알아야 하며, 이를 알기위해 Inspector 사용이 필수적이다. 아래와 같이 이전 버전의 Appium GUI App을 사용해 1.6번의 Appium을 기동시키고, Inspector는 1.5.3 버전을 사용하도록 한다.
      • Xcode 8.x 이상(iOS >10.0) 사용시, Appium GUI 버전 App은6이상을 사용해야 한다. 현재 1.6은 Inspector를 사용할 수가 없다. 테스트 케이스를 만들기 위해, 화면을 구성하는 Element들의 reference를 알아야 하며, 이를 알기위해 Inspector 사용이 필수적이다. 아래와 같이 이전 버전의 Appium GUI App을 사용해 1.6번의 Appium을 기동시키고, Inspector는 1.5.3 버전을 사용하도록 한다.
        • 1.5.3 버전의 Appium GUI App을 실행시킨다. Platform Version에서 10이상의 버전이 존재하지 않는 경우 수기로 버전을 적어 넣는다.
          스크린샷 2017-04-16 오후 9.21.19
    • Xcode 8.x 이상(iOS >10.0) 사용시, Appium GUI 버전 App은6이상을 사용해야 한다. 현재 1.6은 Inspector를 사용할 수가 없다. 테스트 케이스를 만들기 위해, 화면을 구성하는 Element들의 reference를 알아야 하며, 이를 알기위해 Inspector 사용이 필수적이다. 아래와 같이 이전 버전의 Appium GUI App을 사용해 1.6번의 Appium을 기동시키고, Inspector는 1.5.3 버전을 사용하도록 한다.
      • 1.5.3 버전의 Appium GUI App을 실행시킨다. Platform Version에서 10이상의 버전이 존재하지 않는 경우 수기로 버전을 적어 넣는다.
      • GUI버전에서 Launch를 시키지 않고, 터미널 프로그램을 통해 6 이 설치된 디렉토리로 이동하여 command line상에서 Appium을 기동시킨다.
        • 아래와 같은 에러가 발생하는 경우, GUI의 doctor 프로그램을 실행하여 GUI 버전을 통해 설치된 화일들 중에서 해당 에러를 표시하는 화일들을 찾아 에러가 나지 않도록 수정한다. 그리고 Xcode를 업데이트 해준다.(상세 내용은 http://stackoverflow.com/questions/40129794/how-to-fix-error-could-not-detect-mac-os-x-version-from-sw-vers-output-10-12/40168992#40168992 참조)GUI버전에서 Launch를 시키지 않고, 터미널 프로그램을 통해 6 이 설치된 디렉토리로 이동하여 command line상에서 Appium을 기동시킨다.Error : Could not detect Mac OS X Version from sw_vers output: ’10.x.x
          • 아래 이미지처럼 GUI에서 빨간색 부분을 클릭하여 Doctor program 실행
            inspector
          • Doctor 실행 결과를 터미널을 띄워 알려준다.
            doctor
          • 빨간색으로 표시된 파일들을 찾아 에러가 발생하지 않도록 10.x 대 버전을 추가해 준다.
            error patch
          • Xcode 업데이트
            스크린샷 2017-04-04 오후 6.21.44
          • GUI  버전 App을 통해 Doctor를 한번 더 실행하여 문제 해결되었는지 확인한다.
            스크린샷 2017-04-16 오후 10.24.03

Appium Server 환경 구성하기

  • Nodejs, NPM(https://nodejs.org/download/)
    •  homebrew 사용하거나 소스를 받아 Node를 설치한다. Node를 설치하면 NPM이 함께 설치된다.
      nodejs
  • Appium(http://appium.io)
    • npm을 사용하거나 설치화일을 다운로드 받아 Appium과 Appium과 dependency있는 모듈들을 한번에 설치한다.
      appium install
    • Appium 실행할 수 있는지 dependency를 체크한다.
      터미널 프로그램으로 실행 가능하다.
      appium doctor
    • Appium Server 실행
      Appium 서버는 터미널 프로그램을 통해 실행시킬 수 있다.
      스크린샷 2017-04-19 오후 3.06.01

Appium 테스트 코드 개발 환경 구성하기

Appium스크립트는 다양한 언어로 개발될 수 있다. 본 사용기에서는 자바 개발 환경을 설치할 것이다.

Appium 스크립트는 화면을 구성하는 구성요소에 사용자 액션(click, swipe….)을 발생시키는 코드의 집합이다.

그러므로 테스트 스크립트 작성을 위해서 앱을 구성하는 구성요소들의 정보를 알아야 한다. 이를 위해 Application의  화면을 구성하는 구성요소들의 정보를 확인할 수 있는 Inspector를 설치해야 한다.

  • Java SDK설치 (http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html)

    • 자바로 테스트 코드를 작성하기 위해서는 Java SDK 다운로드하여 설치한다.
      java
    • Java 환경 변수 설정
      환경 설정 파일에 JAVA_HOME 환경변수를 만들고 설치된 SDK Home을 설정해 준다 설정된 값이 적용될 수 있도록 source 명령어를 실행한 후 설정되었는지 확인한다.
      java path
  • JAVA IDE 설치 (Eclipse, https://eclipse.org/downloads/)
    IDE는 코딩디버그컴파일배포 등 프로그램 개발에 관련된 모든 작업을 하나의 프로그램 안에서 처리하는 환경을 제공한다. IDE를 다운로드 받아 압축을 풀면 eclipse 디렉토리가 보인다. 디렉토리 아래 eclipse App화일을 더블클릭하여 실행시킨다.
    eclipse
  • Maven설치 (https://maven.apache.org/source-repository.html)
    Maven은 Java 코드를 위한 빌드도구이다. homebrew를 사용하거나 소스를 다운     받아 maven을 설치하고 환경 변수를 설정한다. 설정된 값이 적용될 수 있도록 source 명령어를 실행한다. 환경변수 값이 제대로 설정되었는지 확인한다.maven
  • Eclipse를 위한 Maven Plugin설치 (http://download.eclipse.org/technology/m2e/releases/)
    Eclipse에서 Maven을 사용하기 위해 Eclipse를 위한 Maven Plug in을 설치한다. Eclipse의 Eclipse Help > Install New Software… 서브 메뉴를 통해 설치한다.genymotion for eclipse
  • Inspector 설치
    어플리케이션 화면을 구성하는 구성요소들에 대한 정보를 얻기 위해 Inspector 프로그램을 사용할 수 있다. Appium GUI App은 Appium Server 기능 외에 iOS 앱을 위한 Inspector기능 또한 제공한다. 

    • http://appium.io에서 Appium을 설치한다.guiappium
    • Launchpad (응용 프로그램) 에서 Appium UI 버전 앱 아이콘을 확인할 수 있다.
      icon
    • Appium GUI App실행 결과이다.
      appium gui functionsDoctor 프로그램을 실행시켜 Appium 실행을 위해 필요한 라이브러리들이 설치되었는지 필요한 설정이 정상적으로 되었는지 확인하고, Inspector 프로그램을 실행시켜 연결된 device/emulatord에서 실행중인 App을 미러링 하여 App의 화면 구성요소들에 대한 정보를 얻을 수 있다. iOS App 실행 환경 설정 프로그램을 실행하여 연결된 device/emulator에서 실행되어야 할 App의 위치 및 설정 정보를 셋팅할 수 있다. Appium 서버 실행 프로그램을 사용하여 연결된 GUI 프로그램이 내장하고 있는 Appium 서버를 실행시킬 수 있다.

Appium 테스트 코드 개발 환경 구성하기

  • 테스트 앱 준비
    테스트 케이스를 개발하기 위해서 iOS용 앱이 필요하다. 아래 위치에서 앱 소스를 다운로드 받고 컴파일 해보도록 한다.https://developer.apple.com/sample-code/swift/downloads/09_PersistData.zip음식 메뉴를 등록하고 해당 메뉴에 대한 Score를 등록하는 앱이다. Swift로 작성되어 있으며 아래 사이트 App 개발에 대한 step by step 이 상세하게 나와 있다.https://developer.apple.com/library/content/referencelibrary/GettingStarted/DevelopiOSAppsSwift/index.html#//apple_ref/doc/uid/TP40015214-CH2-SW1

    Swift는 정적 바인딩을 제공하는 언어로 코드 상에서 런타임시 발생할 수 있는 Null 체크 같은 오류에 대한 안전성은 확보되지 않지만, 정적인 타입 체크를 컴파일 시간에 잡을 수 있어서 프로그램 작성시에는 좀 불편할 수 있으나 동적 바인딩 기능을 제공하는 언어에 비해서 안정성을 제공한다는 점이 좋은 거 같다.

    Xcode로 앱 소스 코드를 불러와 컴파일 해보자

    open swift app
    소스 코드를 오픈 한다.
    open xcode prj

    iPhone 6 에뮬레이션에서 실행할 것으로 설정한다.

    set_device_emulator

    Clean & Build & 실행한다.

    build

    실행시 아래와 같은 화면을 볼 수 있다. 세 개의 음식에 대한 Score가 등록되어 있다.

    스크린샷 2017-04-17 오후 3.45.08

    Edit을 클릭하면, 아래와 같은 화면이 나와 등록된 음식에 대한 평가 내용을 지울 수 있다. 지우고 싶은 평가에 대해 – 버튼을 누르고  Done을 클릭한다.

    스크린샷 2017-04-17 오후 4.17.32

    + 버튼을 클릭하면, 아래와 같은 화면이 나와 음식에 대한 평가를 등록할 수 있다. 음식명, 음식 사진, 그리고 별 모양을 눌러 점수를 등록한 후 Save 버튼을 클릭한다.

    스크린샷 2017-04-17 오후 4.17.47

  • 테스트 코드 작성
    • 테스트 소스 구조 설정software architecturePageObject 패턴을 사용한다. PageObject 패턴은 화면 단위로 어플리케이션을 모델링하여 테스트할 수 있게 해주며, 시나리오와 실제 로직 사이의 디펜던시를 없애 유지보수가 용이하고 중복되는 코드를 없애는데 효과적이다. 동일한 테스트 코드로 서로 다른 플랫폼의 GUI 기반 테스트를 하는 Appium에 적합하다. 관련하여 selenium에서 Page 객체를 인스턴스화하기 위한 PageFactory API를 제공한다.화면의 종류에 따라 FoodTrackerMainPage, FoodTrackerRegisterPage, PhotoPage 세 개의 Page를 가지며, 각각은 Interface Type의 형태를 가진다. 모든 Interface는 IOS, Android 용의 구현화일에 의해 구현된다. 각 구현화일은 xpath, name 으로 Page에 속한 UI구성요소에 대한 레퍼런스를 가지고 있으며, 레퍼런스를 리턴하거나 각 플랫폼 의존적인 동작을 하는 API를 가진다.

      테스트 시나리오 화일은 각 Page에 대한 인스턴스를 가지고 있으며, 인스턴스에 정의된 메소드를 가지고 테스트 시나리오를 구성하고, 결과를 검증한다. 인스턴스는 Interface Type으로, IOS와 Android에 따라 구현된 구현 클래스를 객체화한 변수이다. Interface Type을 가지기때문에 IOS, Android 어떤 플랫폼이나 동일하게 가지는 메소들로 구성된다.

    • 테스트 시나리오 작성
      테스트는 시나리오는 다음과 같다. 테스트 시나리오 작성시 모든 화면을 구석 구석 들어가보고 각 화면의 모든 UI 구성요소에 모든 액션들을 발생시켜 보도록 작성하고 액션 발생순서 또한 다양하게 해보는게 중요한 것 같다. 하지만 예제에서는 각 페이지를 들어가 보고 눈에 띄는 액션들만 한번씩 발생시키는 것을 목표로 했다.

      1.  애플리케이션을 실행시킨다. Main 화면을 구성하는 UI 요소들이 정상적으로 표시되었는지 확인한다.

      2. Edit 버튼을 눌렀을때 음식 메뉴를 삭제할 수 화면이 표시되는지 확인한다. 다시 한번 눌러 삭제 모드에서 나오는지 확인한다.
      3. 삭제 버튼을 클릭하여 지정된 음식이 삭제되는지 확인한다. 현재 등록된 음식을 하나씩 모두 삭제한다.

      4. 음식에 대한 평가 20개를 하나씩 등록한다.

      5. 화면을 아래로 스크롤 한다.(10회)

      6. 화면을 위로 스크롤 한다.(10회)

    • 테스트 코드 작성
      아래에서 설명하는 테스트 코드는 https://github.com/heeseon/IOS_SWIFT_Appium_Example 에서 받을 수 있다.

      • maven  프로젝트를 생성하고, pom.xml을 열어 디펜던시가 있는 junit, appium java-client, selenium-java 라이브러리를 설정한다.
        pom file
      • JUnit 테스트 화일은 테스트 초기화, 해제 코드 그리고 테스트 함수들이 어노테이션과 함께 작성된다. 테스트 화일은 테스트 시작, 해제 함수를 갖는 화일과 테스트 시나리오를 갖는 화일로 분리했다.
        테스트 시작, 해제 함수는 테스트 시작시, 테스트 완료시 한번만 호출되도록 @BeforeClass, @AfterClass 어노테이션과 함께 사용했다.
        testfile-start-end
        setUp 함수는 Controller의 start 코드를 호출한 후 각 Page에 대한 인스턴스를 생성한다.
        setUp과 tearDown에서 호출하는 AppiumController의 코드는 다음과 같다.
        testfile-controller테스트 시작시 불리워지는 start 함수에서는 Appium 서버에 테스트 정보를 전달하고 세션을 얻어와 driver라는 변수에 저장한다. 서버에 전달되는 테스트 정보는  DesiredCapability라는 타입의 객체로, 테스트 앱 플랫폼, 디바이스 버전, 테스트 하고자 하는 앱 화일(xxx.app) 패스, automationName(XCUITest) 등의 정보가 최소한 설정되어야 한다. iOS 9.3 이상용으로 컴파일된 앱은 automationName을 반드시 XCUITest로 지정해 주어야 한다.
        Xcode로 App을 빌드하면 결과 화일(app 확장자 화일)이 저장되는 디폴트 위치는 App의 소스 코드 패스가 아닌 Xcode 패스내에 저장된다. 저장위치는 아래와 같은 절차를 통해 확인할 수 있다.Xcode의 Preferences…를 클릭한다.app path 1

        Location 탭의 Derived Data 정보를 확인한다.

        app path 2

        Finder를 열어 Derived Data 위치에 가면 앱 프로젝트 이름에 뒤에 이상한 스트링이 붙은 폴더가 있고, 그 아래 Build>Products>Debug-iphonesimulator 폴더 아래 app 화일을 확인할 수 있다.
        appfilepath

        테스트 완료 시 불리워지는 stop함수에서는 세션을 해제하도록 driver에 quit 함수를 호출한다.

        % DesiredCapabilities는 key, value를 가지는 hash 구조를 가지며, 자동화를 수행하는 동안 테스트하기 원하는 세션의 정보를 주거나, 기능에 대한  timeout 시간을 설정하는 등과 같이 Appium 서버를 제어하기 위한 속성값을 설정할 때 사용한다. iOS와 Android는 같은 key에 대해 서로 다른 value를 가진다. 상세 사항 은 아래에 명시된 API에 대한 reference를 참고하기 바란다.
        https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/remote/DesiredCapabilities.html
        %

      • 각 화면(Page)에 대한 구현 클래스를 구현하고 인스턴스를 생성한다.
        세 화면을 위해 FoodTrackerMainPage, FoodTrackerRegisterFoodPage, PhotoPage Interface를 생성하였다.
        각각의 Interface는 화면을 구성하는 UI 구성 요소를 리턴하는 메소드와 화면에 로딩 시, 표시되어야 하는 UI 리스트를 리턴하는 API를 정의하였다. 그리고 XCUITest프레임워크에서 구현한 IOSElement의 findElements 함수가 정상적인 역할을 하지 못하는 것 같아 xpath를 통해 자식 노드들을 검색해주는 API를 정의해 주었다.
        테스트 화일에서 각 Page에 정의되 API와 MobileElement가 제공하는 API를 호출해 테스트 시나리오를 실행시킨다.
        각 Page를 구현한 구현클래스에서는 화면을 구성하는 UI 구성 요소들에 대한 reference를 정의하였다.  생성자 함수에는 MobileElement초기화를 위해 PageFactory.initElements 함수를 호출해 주고, 각 reference에 xpath와 함께 @FindBy를 달아 MobileElement라는 것을 명시해줬다.화면의 UI 구성요소의 xpath를 포함한 정보를 얻기 위해서는 Inspector를 사용해야 한다.
        Appium GUI 버전을 띄워 App의 정보(App 패스 및 실행될 iOS 플랫폼 정보)를 설정해 주고 Inspector 기능을 실행시킨다. Simulator가 실행되고 Simulator에서 App이 실행된다.스크린샷 2017-04-17 오후 3.45.08
        그리고 Inspector가 떠서 Simulator 화면을 미러링 한다.
        inspector1
        가끔 device/simulator 화면 정보를 제대로 읽어오지 못하는 경우가 있다. 이런 경우 Refresh 버튼을 사용하여 문제를 해결할 수 있다.

        미러링 화면에서 특정 UI 요소를 클릭하면 그 UI 정보가 Detail 화면에 나타나고 그 UI의 계층 구조내에서의 위치가 화면 왼쪽 계층 구조내에서 나타난다.  혹은 왼쪽 계층 구조내에서 특정 요소를 클릭하면 그 UI를 미러링된 화면에서 붉은색으로 보여준다.  아래 화면은 오른쪽 미러링 화면에서 두번쨰 음식 평에 별 모양을 클릭했을 때 별 UI 정보의 상세 정보와 계층 구조내의 위치를 보여주는 예제이다.
        inspector usage
        위와 같이 Inspector에서 특정 UI를 선택해 Details 정보 내용을 확인하여 xpath나 UI 구성요소의 속성값을 알아내고 이것을 테스트 코드에서 사용한다.
        이렇게 얻은 UI 구성요소의 정보를 가지고 그것의 레퍼런스를 알아내고 레퍼런스에 아래와 같은 사용자 액션을  실행시킨다.
        – click/ tap
        – setText/getText
        – swipe
        위와 같은 사용자 액션 외에도 Appium은 로그 수집이라든지, crash 수집 등의 기능을 사용할 수 있다. 자세한 내용은 아래 API 설명을 참고하자
        http://appium.github.io/java-client/

      •  

        Inspector는 이외에 특정 UI 구성요소에 Tap/Swipe/Shake/Text 입력 등의 사용자 액션을 발생시키는 기능과 이렇게 발생시킨 사용자 액션을 테스트 코드로 저장해 주는 기능도 있다.  사용해 보면 테스트 코드 작성시 도움이 될 듯 하다.

      • 시나리오의 각 단계를 구현한다.
        위에서 정의한 시나리오 각 단계를 @Test 어노테이션과 함께 테스트 함수로 구현해 주었다. 각 Page에서 정의한 인터페이스를 통해 화면 UI 구성요소의 reference를 얻고, MobileElement에 정의된 API를 호출해 reference에 사용자 action함수를 발생시킨다. 그리고 화면에 변화가 생겼는지 MobileElement가 제공해주는 API를 사용해 검증한다.

사용후기

요즘 모바일 디바이스 앱의 호환성 및 시나리오 테스트를 위해 가장 관심도가 높은 Appium에 대해서 사용해 본 결과 느낀점을 적어보았다. 

  1. 당연히 UI 테스트로 소프트웨어의 모든 기능 테스트를 하는 것은 불가능하다. 

기본적으로 모든 유닛에 단위 기능 테스트가 작성되어야 하며, 단위 테스트는 입력에 대한 기능을 정상적으로 하는지 출력을 정해진 대로 내는지 입력에 대한 예외 처리를 제대로 하는지 유닛 내부에 모든 패스에 대해서 테스트가 되는지를 기준으로 커버리지가 산정되어야 하며, 커버리지가 100에 가까울 수 있도록 테스트가 되어야 할 것이다. 그러나 이렇게는 시간이나 비용이 많이 드니 최소한의 테스트인 시나리오 테스트를 하는 것 아닐까 싶다. 단위 기능 테스트가 100프로 되었다고 해서 시나리오 테스트가 필요 없다는 이야기는 아니다.  

애니웨이, UI 테스트는 시나리오 테스트에 적합하며, 짧은 개발 시간에 애플케이션의 테스트를 진행하기 위해 파편화가 심한 모바일 디바이스 환경하에서 다양한 디바이스 간의 호환성 테스트하는 것에 적합하다.

2. 오픈소스인 Appium에 개발자들이 기여할 수 있는 부분이 많다.

소소한 이슈들이 많아 해결되어야 하며, 개발 시간 단축을 위해 사용하는만큼 테스트 환경 및 케이스 개발을 쉽게 할 수 있도록 하는 기능들, 테스트 결과를 쉽게 확인할 수 있도록 하는 툴이 제공되어야 한다.  툴은 Appium framework에 속한 내용일 수도 있고,  클라이언트 라이브러리와 함께 IDE에서 제공하는 형태일 수도 있고, 디바이스에서 테스트시 실행되는 또다른 애플리케이션일 수도 있고, 애플리케이션에서 사용자 액션을 저장할 수 있도록 혹은 테스트 시 어플리케이션과 통신을 위한 어플리케이션에 내장되는 라이브러리 일 수도 있겠다.

3. UI 테스트라 시간이 오래 걸린다.

개발 시간에 영향받지 않도록 별도의 프로세스로 자동으로 주기적으로 돌리고, 돌리는 동안 다른 일을 하고 이후에 결과를 확인할 수 있어야겠다. 테스트를 하는 동안 디바이스에서 일어나는 테스트 결과/AV/성능수치를 포함한 외에 모니터링되어야 하는 모든 디바이스 상태가 저장되고 이후에 한 화면에서 이것을 확인할 수 있으면 좋을 것 같다.

Appium으로 성능 분석하며 자동 테스트하기

Appium으로 성능 분석하며 자동 테스트하기

– 성능 분석 지표?
– Appium의 구조 깊이 보기
-> 스마트한 Device Platform에서 제공하는 자동화를 위한 software 모듈
-> UIAutomator, Selendroid, UIAutomation
– 프로파일링 기능은 어디서 처리? App or UIAutomator
– 테스트 결과와 프로파일링 결과 동기화하기
– 하나의 디바이스에서 테스트하고 결과 보기
– 여러 디바이스에서 테스트하고 결과 보기
-> Appium은 동시에 여러 세션을 관리할 수 있을까?
-> 여러 디바이스에서 동일 테스트 돌리기 위한 스크립트 작성하기
-> 테스트 결과 관리하기
-> 테스트 결과 + 프로파일링 결과 정보화 하기
-> 여러 디바이스의 테스트 결과 비교해서 보기, 앱버전에 따른 결과 비교해 보기
-> 사용자를 위한 테스트 결과 관리 메뉴 제공하기
– 프로파일링 기반 UI 테스트 자동화 쉽게 설치 및 사용하기 위한 방법 제시 – Docker or Vagrant

=============================================================

다양한 기능 활용하여 자동화 스크립트 작성하기

다양한 기능 활용하여 자동화 스크립트 작성하기

– 테스트 하기 위한 앱을 작성해보자
– 테스트 자동화 스크립트 작성
-> 자동화 테스트를 위한 셋업 스크립트
-> ….
-> ….
– ….
– 테스트 결과를 기록에 남기기 위해 Runner와 Listener를 작성해보자
– 여러 테스트 화일을 배치처리할 수 있도록 TestSuite를 구현해 보자
– 여러 디바이스에서 동일 테스트 배치 처리할 수 있도록 프로그램 구현해 보자
-> DB 와 View를 붙이기

자동화 예제

view를 붙여 보았다(type 1)

스크린샷 2016-01-24 오후 10.22.52

(type 2)

스크린샷 2016-01-24 오후 10.45.03

우선 결과를 빨리 보기 위해서 사용이 간편한 protovis 라이브러리를 사용했지만, 이후 앵귤러와 궁합이 더 잘 맞는 경량 라이브러리로 붙여봐야겠다

이젠 DB를 붙여보자

 

 

안드로이드 테스팅의 효자손 “Appium(앱피움)” 파헤치기 2

[마이크로 소프트웨어 기고, 2015년 9월 7일]

안드로이드 테스팅의 효자손 “Appium(앱피움)” 파헤치기 2

– Appium 관심도 조사
– BDD가 정답일까
– BDD 자동 스크립트 개발 환경
-> 환경 구성
-> BDD 스크립트 작성 예제 – Predefined Step 사용
-> BDD 스크립트 작성 – Custom defined Step 사용
-> 자동스크립트 실행
-> 스크립트에서 설정 – 자바 클라이언트
-> 커맨드 라인에서 설정 – 루비 클라이언트
-> 이클립스 실행 방식 – Eclipse JUnit Runcher 사용
-> 커맨드 라인 실행 방식 – Cucumber command line 명령어 사용
– 앱피움 인스펙터 – 앱피움 GUI 버전 사용하기
– Appium UI 버전 앱(Appium.app) 설치
-> 디바이스 실행
-> 앱피움 UI 버전 앱 실행
-> 앱 실행을 위한 설정
-> 앱피움 서버 실행
-> 인스펙터 실행
– 앱피움 API

=============================================================

Appium 관심도 조사

google trends(https://www.google.com/trends/?hl=ko)에서 검사해본 UI 자동화 툴 관심도를 보고자 한다(주제어:툴 이름 + android).

비교 툴들은 구글에서 안드로이드 UI 자동화 툴로 검색하면 가장 많이 등장하는 툴들로 구성하였다.

관심도

<그림 1. UI 자동화 툴들의 관심도 변화 비교>

Appium이 다른 툴들에  비해 늦게 등장했지만 꾸준히 관심도가 높아지고 있고 최근에는   가장 높은 관심도를 보이는 것을 알 수 있다.

BDD 가 정답일까?

TDD 와 BDD는 애자일 소프트웨어 개발 방법론에서 가장 널리 쓰이는 테스트 기반 개발 방법론이다. 테스트 시나리오와 테스트를 먼저 만들고 그것을 만족하는 코드를 작성해 나가는 개발방식을 일컫는다. TDD는 상대적으로 테스트에 더 집중하고 BDD는 비지니스 요구사항에 더 집중하여 테스트 코드를 생산한다. 코드가 없는 상태에서 개발 요구사항을 검증하는 코드를 작성해 보면 개발 요구사항을 좀 더 정확히 이해할 수 있으며 요구사항에서 놓친 예외사항을 미리 알 수 있어 더 안정적인 시스템을 만들 수 있다는 장점을 가진다. 그럼 BDD 와 TDD 의 차이점은 무엇일까?  TDD는 테스트 스펙을 머리로 생각하고 테스트 코드 작성에 들어가는데 반해, BDD는 사용자 시나리오 관점에서 자연어에 가까운 언어로 이 과정을 기술하는 단계를 가진 후 TDD의 개발 과정을 밟는다. BDD는 테스트 시나리오가 자연어에 가깝기때문에 디자이너, 개발자, 테스터가 모두 동일하게 시스템의 스펙을 바라볼 수 있다는 점과  대개 BDD 방식의 테스트 툴이 미리 정의된 코드들을 지원하여 이를 사용하면 자연어에 가까운 언어만으로 테스트 코드 작성이 가능하여 테스트 개발이 쉽고 빠르다는 장점이 있다. 테스트는 경제적인 코드 생산을 위해 하는 작업이고 어느 레이어의 코드냐 코드의 목적이 무엇이냐에 따라 테스트 범위, 방법이 달라진다. 그리고 테스트 비용을 유발시키기 때문에 상황에 따라 BDD가 적합할 수도 있고, TDD 방식이 더 적합할 수 있을 것이다.  고객이 알아야 하는 사항들에 대한 테스트는 BDD 를 고객 요구사항을 구현하기 위해 고객은 모르지만 내부적으로 개발해야 하는 기능들에 대한 테스트는 TDD 가 좋다.  애피움의 큰 장점 중 하나는 두 가지 개발 방법론을 다 지원하기 때문에 상황에 맞게 테스트 개발 방법론을 선택할 수 있다. 

BDD 자동 스크립트 개발 환경

BDD 방식의 자동화 스크립트는 [프로젝트 기본 파일 구조(feature) 생성]->[테스트 시나리오 및 스텝 작성]->[스텝 구현] 절차를 따라 작성된다. 

시나리오 작성 및 스텝 작성 단계에서 스텝은 구현이 이미 작성된 경우(predefined step)도 있고,  작성되지 않아 개발자가 작성해 줘야 하는 경우(custom defined step)도 있다. 

이번 장에서는 두 가자 경우에 대해서 다 살펴볼 것이다. 전자는 appium client를 Ruby 언어로 테스트 프레임워크로 Cucumber를 사용할 것이며,  후자는 appium client를 자바 언어로 테스트 프레임워크를 Cucumber와 JUnit으로 사용할 것이다.

설치

1. Ruby

스크립트 개발 언어 Ruby를 아래와 같이 설치한다. 

ruby install

<그림 2. Ruby 설치>

2. selenium-cucumber

Cucumber는 BDD를 지원하는 고수준 테스팅 툴이다. Selenium은 웹을 자동화하기 위한 테스트 툴이다.  selenium-cucumber는 Web과 안드로이드 앱의 테스트 자동화를 위한 툴로 관련 Dependency가 있는 모듈들을 모두 설치해 준다.

seleniumcucumber

<그림 3. selenium-cucumber 설치>

외에  Java, Android SDK, Appium 이 설치되어야 한다.  이전 회에서 설치 과정을 명시했으므로 이전 회를 참고하기 바란다.

BDD  테스트 자동화 스크립트 작성

[1]  Predefined Step 을 사용하여 스크립트 작성

appium client를 Ruby로, 테스트 프레임워크를 Cucumber를 사용한다. 자동화 스크립트 작성 및 실행은 IDE를 사용하지 않고 커맨드라인으로 작업을 한다.

프로젝트를 담을 폴더를 생성하여 테스트하고자 하는 앱을 위치시키고,  selenium-cucumber툴을 사용하여  feature skeleton을 생성하자 . feature skeleton은 아래 그림의 설명처럼 애플리케이션을 테스트 자동화하기 위한 기본적인 파일 구조를 가지는 하나의 프로젝트라고 보면 된다.  

seleniumcucumberapp

<그림 4. feature-skeleton 생성>

feature skeleton은 아래와 같은 파일 구조를 가진다.

 

tree

                                <그림 5. feature-skeleton의 화일 구조>

프로젝트를 구성하는 화일들의 상세 사항은 아래와 같다.
– my_first.feature : 사용자 스토리(비지니스 요구사항)와 요구사항을 테스트하는 다수의 테스트 시나리오와 각 시나리오를 구성하는 다수의 테스트 스텝을 가지는 파일이다.
– step_definition 폴더 : my_first.feature 에서 시나리오를 구성하는 테스트 스텝은 미리 정의된 구현 코드를 가지는 스텝일 수도 있고, 정의되지 않아 개발자가 구현을 만들어줘야 하는 스텝일 수도 있다. 정의되지 않은 스텝일 경우, 스텝에 대한 구현 코드 파일을 가지는 폴더이다
– support 폴더 : 테스트 진행 시, 로드되는 파일들로, 테스트 코드 작성할 때 개발자가 별도로 만든 Ruby 파일들을 사용하고자 할 때, 혹은 각 테스트 전 후에 공통적인 코드를 추가하고자 할때 이 폴더 내의 파일에 코드를 추가한다.   
– actual_images, expected_images, Image_difference, screenshots 폴더 : 테스트 도중 캡쳐한 스크린 샷이나 테스트 할 때 사용하는 이미지 등을 가지는 폴더이다

predefined step을 사용하거나 혹은 자신만의 스텝을 만들어 feature 파일(feature/my_first.feature 파일)에 스텝을 정의한다

———————————————————————————————

Feature: Login feature
  Scenario: As a valid user I can log into my app
        When  I enter "domich" into input field having id 
                                  "com.example.simplelogin:id/idET"
        When I enter "1234" into input field having id 
                                 "com.example.simplelogin:id/pwdET"
        When I click on element having id 
                                 "com.example.simplelogin:id/login"
        Then element having id 
  "com.example.simplelogin:id/result" should have text as "Logined"

———————————————————————————————
<코드 1.  루비 클라이언트 사용 – Login Feature 파일 코드>

Feature는 사용자 스토리를 의미하며, 비지니스 요구사항을 담는다.

Scenario는 다수개가 올 수 있으며 각각 아래와 같이 Given/When/Then 템플릿으로 시나리오를 검증하기위한 테스트 스텝을 갖는다.

Given 검증 전 준비 단계 어떤 상태 등을 나타낸다
When 사용자가 특정 액션을 취했음을 나타낸다
Then 액션에 대한 결과가 어떠해야 함을 나타낸다

예제 샘플 스크립트 내용은 다음과 같다. 하나의 시나리오를 가지며 그 시나리오는 네개의 스텝으로 구성되어 있다.  사용자가 idET요소에 domich을 pwdET요소에 1234를 입력하고,  login 요소를 클릭했을 때, 화면 상에 result 요소에 Logined라고 표시되는지를 확인하는 유효한 사용자의 로긴 시나리오이다.

각 요소의 id 찾는 방법은 지난호에 기재된 UIAutomatorViewer 나  뒷면에서 나오는 원격 디버깅 툴 사용법을 참고하기 바란다.

3. 2번 단계에서 predefined step을 사용하지 않고 custom step 으로 작성하였다면, custom step을 구현해 줘야 한다.(feature/steep_definitions/custom_steps.rb 파일)

이 예제는 predefined step을 사용하였으니 이 부분을 생략한다.

위 테스트 코드는

https://github.com/heeseon/RubyAppiumUITestScript_BDD_forSimpleLoginApplication

에서 얻을 수 있다.

[2]  Custom defined Step 을 사용하여 스크립트 작성

appium client를 JAVA로, 테스트 프레임워크를 Cucumber와 JUnit을 사용한다. 자동화 스크립트는 Eclipse의 Maven 프로젝트 형태이며, 실행은 JUnit으로 한다. Maven 프로젝트에 대한 설명은 지난 호에서 살펴봤으므로 자세히 하지 않는다. 

Maven 프로젝트를 생성한다.

아래와 같이 pom.xml 화일에 cucumber를 사용하기 위한 dependency를 추가한다.

스크린샷 2016-01-11 오후 7.33.59

<코드 2. BDD 방식의 스크립트 작성을 위한 appium dependency>

3. 프로젝트 루트 폴더 아래 apps 폴더를 생성하고 테스트하고자 하는 앱을 위치시킨다.

4. src/test/java폴더 아래 feature 폴더를 생성하고 feature화일(Login.feature)을 생성한 후, 아래와 같이 스크립트를 작성한다.

동일한 앱을 테스트를 하는 것이므로 위에서 생성한 Ruby용 스크립트와 내용이 유사하다.  문법은 동일하게 Given/When/Then 템플릿을 사용한다.

———————————————————————————————

Feature: Invalid Login to the SimpleLogin app
Scenario: Passing valid credentials and log in into the application
    Given I am on the landing page
    When I enter id as "domich" 
    When I enter password as "1234"
    When I click login button
    Then I should see the success message "Logined"

———————————————————————————————

<코드 3.  자바 클라이언트 사용 – Login Feature 파일 코드>

5. src/test/java폴더 아래 steps 폴더를 생성하고 스텝 구현 파일(LoginSteps.java)을 생성한다.

지난 호에 살펴본 TestNG 구조와 유사하게 테스트 전처리(@Before), 후처리(@After)를 위한 Anotation을 사용하며, 외에 Given/When/Then 구문에 대한 Anotation을 사용하여 작성된다.

시나리오 파일에서 작성된 스텝들 각각에 대한 구현이 있으며,  각 스텝과 테스트 전, 후처리 구현에 대한 설명은 지난 호에 작성된 것과 유사하기때문에 지난 호의 내용을 참고하기 바란다.

———————————————————————————————

public class LoginSteps {
    private AndroidDriver driver;
    By userId;
    By password;
    By login_Button;
    By result;
     
    public LoginSteps() {
    }
    
//자동화 테스트 전처리
@Before
public void initializeAppiumSession() throws MalformedURLException{
      File appDir = new File(
   "/Users/hwangheeseon/Downloads/AppiumCucumberDemo-master/apps");
      File app = new File(appDir, "app-debug.apk");
      DesiredCapabilities capabilities = new DesiredCapabilities();
      capabilities.setCapability("deviceName","Android Emulator");
      capabilities.setCapability("automationName", "Selendroid");
      capabilities.setCapability("platformName","Android");
      capabilities.setCapability("app", app.getAbsolutePath());
      driver =  new AndroidDriver(new URL("http://127.0.0.1:4723/wd/hub"), capabilities);
}

//Given Anotation, ^와  $는 스텝 구문을 감싸는 문법
@Given("^I am on the landing page$")
public void I_am_on_the_landing_page() throws Throwable {
    
      userId = By.id("idET");
      password = By.id("pwdET");
      login_Button = By.id("login");
      WebDriverWait wait = new WebDriverWait(driver, 60);
      wait.until(ExpectedConditions.visibilityOfElementLocated(userId));
}
//When Anotation, 
@When("^I enter id as \"([^\"]*)\"$")
public void I_enter_id_as(String ID) throws Throwable {
      driver.findElement(userId).sendKeys(ID);
}
//When Anotation,
@When("^I enter password as \"([^\"]*)\"$")
public void I_enter_password_as(String PW) throws Throwable {
      driver.findElement(password).sendKeys(PW); 
}
//When Anotation,
@When("^I click login button$")
public void I_click_login_button() throws Throwable {
      driver.findElement(login_Button).click(); 
}
//Then Anotation,
@Then("^I should see the success message \"([^\"]*)\"$")
public void I_should_see_the_success_message(String success) throws Throwable {
 result = By.id("result");
 WebDriverWait wait = new WebDriverWait(driver, 60);
 wait.until(ExpectedConditions.visibilityOfElementLocated(result));
 success.contentEquals(driver.findElement(result).getText());
}
//테스트 후처리
@After
public void tearDown(){
    driver.quit();
    }

}

———————————————————————————————

<코드 4.  자바 클라이언트 사용 – 커스텀 스텝 구현 파일 >

6. 아래와 같이 src/test/java폴더 아래 JUnit 실행 파일(RunFeatures.java)을 생성한다.

——————————————————————————————

//JUnit의 실행 클래스 지정하는 Anotation
@RunWith(Cucumber.class)
// Cucumber의 커맨드에 대한 파라미터 옵션, feature : feature 폴더 패스, format :  실행 결과 표시 포맷 
@CucumberOptions(features = "src/test/java/features/",
        format = {"pretty", "html:target/cucumber", "json:target/cucumber-report.json"})

public class RunFeatures {

}

———————————————————————————————

<코드 5.  자바 클라이언트 사용 – 스크립트 실행 파일>

Cucumber 커맨드에 대한 파라미터 옵션이다. feature는 feature 폴더의 패스를 의미하고  format 은 실행 결과에 대한 표시 포맷을 의미한다. 위에서 사용된 Cucumber  파라미터 옵션의 상세 설명은 아래 사이트를 참조한다.

http://cucumber.github.io/api/cucumber/jvm/javadoc/cucumber/api/CucumberOptions.html

위 테스트 코드는

https://github.com/heeseon/JavaAppiumUITestScript_BDD_forSimpleLoginApplication

에서 얻을 수 있다.

자동스크립트 실행 

1. 안드로이드 디바이스에서 자동화 스크립트 실행하기

Login 샘플을 안드로이드 디바이스에서 직접 실행해 보자

디바이스에서 실행하기 위해 아래의 과정이 필요하다.

  • 디바이스에서 USB 디버깅 기능 활성화하기
  • adb 로 디바이스 인식하기
  • 자동화 스크립트를 실행할 디바이스 선택하기
  • Appium 서버 실행하기
  • Automation script 실행하기

한 단계씩 진행해 보자

디바이스에서 USB 디버깅 기능 활성화하기

안드로이드 디바이스의 USB 디버깅 옵션을 켜기 위해 설정에서 개발자 옵션 메뉴에 들어가자

개발자 옵션이 숨겨진 디바이스에서는    [환경설정]>[더보기]>[디바이스 정보] 메뉴로 진입해 빌드 번호 정보를 손가락으로 7번 탭핑한다.

개발자 옵션이 활성화되면 그림 1과 같이  [환경설정]>[개발자 옵션] 에 진입하여 USB 디버깅 기능을 활성화 시키자

activatedevice

<그림 6. USB 디버깅 활성화>

adb 로 디바이스 인식하기

디바이스에 usb cable을 연결한 후 콘솔창을 열어 그림 2와 같이 adb 명령어로 adb가 디바이스를 인식했는지 확인하자

device

<그림 7. adb 에 연결된 디바이스>

그림2은 192168.56.101:5555(에뮬레이터) 와 107f8117(안드로이드 디바이스) 두 개의 디바이스가 인식된 것을 알 수 있다.

adb가 USB 를 통해 인식되는 디바이스의 vendor ID 를 인식하지 못하는 경우, 디바이스의 vendor ID를 설정 파일에 추가해 줘야 한다.

디바이스의 vendor ID는

그림 3과 같이 [Mac 메뉴]>[이 Mac에 관하여]>[시스템 리포트…]>[하드웨어]>[USB] 정보를 통해 알 수 있다.

 deviceconfirm

<그림 8. USB 디바이스의 vendor ID 읽기>

그림 4와 같이 vendor ID를 설정파일(~/.android/adb_usb.ini)에 추가하고 adb를 재실행시킨다.

register device

<그림 9.  vendor ID 추가를 위한 명령어와 설정파일 내용>

자동화 스크립트를 실행할 디바이스 정보 설정하기

디바이스 정보를 스크립트에서 설정하는 경우와 커맨드 라인에서 설정하는 경우 두가지가 있다.

<스크립트에서 설정-자바 client 사용하는 경우>

지난 호에 작성했던 것 처럼 mandatory 항목의 Desired capability만 설정하면 Appium은 디바이스 인식을 못해 무조건 먼저 실행된 디바이스(에뮬레이터) 상에서 자동화 스크립트를 실행시킨다.  아래와 같이 adb가 인식하는 디비이스의 udid(adb devices 명령어를 통해 본 ID)를 추가해 원하는 디바이스에서 스크립트를 실행하자

udid

<그림 10.  디바이스 구분을 위한 DesiredCapabilities 설정>

<커맨드 라인에서 설정-Ruby client사용하는 경우>

커맨드 라인 실행 방식에서는 스크립트가 아닌 커맨드 라인 실행 명령어로 디바이스를 선택한다.

스크립트 실행 명령어인 cucumber 명령어는 현재 adb에 연결된 디바이스를 확인하고 디바이스가 하나인 경우 그 디바이스에서 스크립트를 실행시킨다. 그러므로 아래와 같이 adb devices 명령어로 연결된 디바이스 리스트를 확인하고 원하는 디바이스 외에 다른 디바이스는 adb disconnect 명령어로 연결 해제시킨다. 원하는 디바이스가 연결되지 않은 경우 adb connect 명령어로 디바이스를 연결한다.

Appium 서버 실행하기

콘솔을 통해 appium server를 실행시킨다.(실행방법은 지난 호 내용 참조)

Automation script 실행하기

[ Eclipse IDE  실행 방식 – Eclipse JUnit Runcher 사용]

아래와 같이 Eclipse JUnit  View를 통해 실행 과정을 보여준다.

ide

<그림 11. 자동화 스크립트 실행 과정-IDE사용>

실행이 끝난 후, JUnit 실행 파일(RunFeatures.java)에 기재된 Cucumber report 포맷에 따라 아래와 같이 화면에 실행 결과를 표시한다.

result

<그림 12. 스크립트 실행 결과-IDE사용>

[커맨드라인 실행 방식- Cucumber command line 명령어 사용]

커맨드 라인 실행 방식에서는 아래와 같이 cucumber 실행 명령어의 파라미터로 플랫폼과 앱의 패스를 전달한다. 

cucumber는 아래와 그림과 같이 시나리오의 각 스텝을 실행시키며 실행결과를 화면에 표시한다.

commandline

<그림 13. 스크립트 실행 과정과 결과-커맨드라인사용>

Device screen Display 고려

테스트 실행 시, 텍스트 필드에 값을 넣으면 스크린 키보드가 화면에 나타난다. login 버튼이 스크린 키보드에 가려진 상태에서 버튼에 클릭 이벤트를 전달하면 이벤트가 전달은 되는데 적용되지 않아 테스트의 마지막 스텝이 Fail난다. 이 문제를 해결하기 위해서는 텍스트 필드에 값을 넣은 후에 키보다가 떠 있다면 키보드를 hide 시키는 스텝이 추가되어야 한다.

자바의 경우 텍스트 처리 후 아래의 구문을 추가한다.

————————————————————-

    @When("^I enter id as \"([^\"]*)\"$")
    public void I_enter_id_as(String ID) throws Throwable {
        driver.findElement(userId).sendKeys(ID);
        driver.hideKeyboard();
    }
    @When("^I enter password as \"([^\"]*)\"$")
    public void I_enter_password_as(String PW) throws Throwable {
        driver.findElement(password).sendKeys(PW); 
        driver.hideKeyboard();
    }

————————————————————-

<코드 6.  스크린 키보드 내리는 코드 추가 – 자바 클라이언트 사용>

루비의 경우,  스크린 키보드 내리는 기능이 predefined step으로 정의되지 않았다. 그러므로 custom step을 정의해서 selenium API나 ruby client가 지원하는 API를 사용하여 스크린 키보드 내리는 기능을 구현해줘야 한다.

우선 feature화일에서 login버튼 누르기 전 단계에 아래와 같이 키보드 내리는 스텝을 추가하자( 빨간색이 추가한 스텝이다)

———————————————————–

Feature: Login feature
  Scenario: As a valid user I can log into my app
        When  I enter "domich" into input field having id 
                                  "com.example.simplelogin:id/idET"
        When I enter "1234" into input field having id 
                                 "com.example.simplelogin:id/pwdET"
        When behindkeyboard
        When I click on element having id 
                                 "com.example.simplelogin:id/login"
        Then element having id 
  "com.example.simplelogin:id/result" should have text as "Logined"
-----------------------------------------------------------

<코드 7. 스크린 키보드 내리는 스텝 추가 – 루비 클라이언트 사용>

feature/step_definitions/custom_steps.rb 파일에 추가한 스텝에 대해 custom step을 정의해 보자. 빨간색은 스텝의 구현 파트를 알리는 문법이다.(predefined 와 custom step 중복 사용 가능하다)

-----------------------------------------------------------
When /^behindkeyboard$/ do
  $driver.hide_keyboard
end
-----------------------------------------------------------

<코드 8. 스크린 키보드 내리는 스텝에 대한 구현 – 루비 클라이언트 사용>

device_is_android란 함수가 정의되지 않았다는 에러가 난다. 라이브러리가 아직 완성되지 않은 상태인 것 같다.

제대로 고치려면 복잡할 거 같아 우선 아래와 같이 문제있는 코드에서 device_is_android 함수를 사용하지 않도록 했다. 무조건 android 디바이스로 처리하도록 수정한 것이다. 하지만 이건 좋지 않은 코드이다. 오픈 소스이니 독자들은 이 부분에 기여해 보는 것도 좋을 것 같다.

-----------------------------------------------------------
      add_endpoint_method(:hide_keyboard, 'session/:session_id/appium/device/hide_keyboard') do
          def hide_keyboard(close_key=nil)
            # Android can only tapOutside.
            if 1 #device_is_android
             return execute :hide_keyboard, {}, { strategy: :tapOutside }
            end
            close_key ||= 'Done' # default to Done key.
            $driver.hide_ios_keyboard close_key
          end
        end
-----------------------------------------------------------

<코드 9. 스크린 키보드 내리는 코드 버그를 수정하기 위한 임시코드 >

이렇게 해서 실행하면 아래와 같이 모든 스텝이 패스하는 것을 확인할 수 있다.

useruby

<그림 14. 스크립트 실행 결과 – 루비 클라이언트 사용>

Appium inspector 앱 사용하기

앱을 사용하여 개발자는 실행중인 디바이스(애뮬레이터)에 애플리케이션을 설치/실행시키고 앱을 구성하는 UI 요소들의 id, class 등의 정보를 얻을 수 있으며, 화면의 특정 구역을 지정하여 변수(xpath)를 정의할 수 있다. 또한 다양한 UI 명령어를 실행시키며 앱의 동작 결과를 확인할 수 있으며, 앱 설치/실행, UI 명령어 실행 등의 모든 실행 과정을 자동화 스크립트로 자동 생성할 수 있다.

Appium UI 버전 앱(Appium.app) 설치
http://appium.io에서 Appium을 설치한다.

guiappium
<그림 15. Appium 설치>

Launchpad (응용 프로그램) 에서 Appium UI 버전 앱 아이콘을 확인할 수 있다.

icon
<그림 16. Appium 실행 프로그램>

디바이스(에뮬레이터) 실행( adb를 통해 디바이스에 연결)

자동화 스크립트를 실행시킬 디바이스나 에뮬레이터를 실행시킨다.

Appium UI 버전 앱 실행
응용프로그램에 Appium 아이콘을 클릭하여 앱을 실행하자 여러 기능이 있지만 자주 사용하는 기능을 빨간색으로 칠하였다.

appiumguiexecution
<그림 17.   Appium UI버전 실행>

3.1. 앱 실행을 위한 설정

애피움 서버에 안드로이드 앱(.apk)의 패스 및 실행 환경 설정 등 자동화 스크립트 실행을 위한 정보를 설정한다.

appiumguisetting

< 그림 18 테스트할 애플리케이션 정보 설정>

3. 2 애피움 서버 실행

애피움 서버를 실행한다. 이전에 커맨드 라인으로 애피움 서버를 실행중이었다면 같은 포트를 사용하기때문에 실행을 실패할 것이다. 실행중인 서버를 중단하고 UI를 통해 애피움 서버를 실행시키자. 아래 그림에서 볼 수 있듯이 애피움 서버가 실행되면서 실행 아이콘이 실행 종료 아이콘(Stop)으로 바뀌는 것을 볼 수 있으며, 애피움 서버의 로그를 볼 수 있다.

serverexcecution

<그림 19. 애피움 서버 실행>

3.3 inspector  실행

인스펙터를 실행시키면, 디바이스(에뮬레이터에) 에 앱을 설치하고 실행시킨다. 그리고 앱에 대한 설정 및 UI 명령어를 실행시킬 수 있는 Inspector 창이 뜬다.

appiuminspector

<그림 20. 애피움 인스펙터>

Inspector 창을 구성하는 각 기능 요소들의 기능은 아래 테이블과 같다.

스크린샷 2016-01-11 오후 8.22.32

<표 1 인스펙터 기능>

웹앱 크롬 원격 디버깅 도구 활용해 UI 요소 ID 찾기

native 앱의 resource id와 locator를 알기 위해 UIAutomator 를 사용했다. 앱이 hybrid나 웹 앱인 경우, UIAutomator 대신 크롬 도구를 활용할 수도 있다.  이 경우 안드로이드 SDK를 설치하지 않아도 되어 가볍고 빠른 속도로 앱의 ellement의 id를 알 수 있고, 앱을 디버깅할 수 있다는 장점이 있다.

디버깅 PC 와 안드로이드 디바이스를 USB 로 연결한 후, PC에서 크롬을 실행시키고 [메뉴]>[도구 더보기]>[기기 검사] 서브 메뉴를 통해 확인할 수 있다. UIAutomator와 같이 디바이스(에뮬레이터) 프리뷰 화면을 지원하고 애플리케이션의 레이아웃을 코드로 부터 쉽게 UI요소를 검색하여 요소 정보를 얻을 수 있다.                                                                                                                                                                                      

Appium API

아피움 서버와의 세션을 설정하고 코드 내에서 UI 요소의 레퍼런스를 찾아 레퍼런스가 현재 화면에 보여지는지 확인하고 UI 요소에 UI 이벤트를 발생시키고 키보드를 내리는 등의 API를 사용해 보았다.

아피움은 이것외 다양한 UI 이벤트(Swipe, Tap, Long Tap, Scroll 들을 발생시킬 수 있으며, Javascript 코드를 실행시키거나 디바이스(애뮬레이터) 스크린을 캡쳐해 저장할 수 있는 API 등 test runner가 지원하는  assertion등을 지원한다. 또한 Selenium API를 사용하여 브라우저의 속성들을 컨트롤 하거나 테스트를 여러 기기에 분산시키기 위한 API가 제공된다.

API에 대한 자세한 설명은 아래 링크를 참고할 수 있다.

selenium api doccumentation, http://www.seleniumhq.org/docs/03_webdriver.jsp

appium api documentation,
http://appium.io/slate/en/master/?ruby#

selenium cucumber api documentation(predefined step, and api), http://seleniumcucumber.info/

결론

지난 호에 이어 BDD 개발 방식에서의 Appium 사용법을 살펴보았다. 짧지만 스크립트를 작성해 보며 느낌 소감은 Appium이 활용도가 높을 것이다라는 전망이다. 단말, 플랫폼, 스크린의 경계가 없어지는 추세로 인해 HTML5가 공통플랫폼 기술로 고려되고 있고, Android, iOS 는 이미 모바일 플랫폼으로 대부분의 디바이스에서 적용되고 있다. 결국 Appium은 가장 핫한 플랫폼들을 모두 커버할 수 있는 UI 테스트 자동화 기술인 것이다.  이와 더불어 요즘 좀 더 쉽고 효율적인 Appium 스크립트 작성을 위해 클라이언트 라이브러리와 개발 도구들이 오픈소스로 개발되고 있다. 오픈소스 및 오픈소스 커미터에 대한 관심이 날로 높아지는 가운데 오픈소스의 커미터가 될 수 있는 좋은 기회가 될 수 있을 것 같다. 쉽고 효율적인 스크립트 작성에 대한 기여 뿐만 아니라 UI 테스트 외에 ,테스트를 하면서 시스템 리소스 분석과 같은 시스템의 품질을 함께 검증해주는 기능들이 자동화 될 수 있을 것이다.

참고

채수원, 테스트 주도 개발 TDD 실천법과 도구

appium, http://appium.io/

appium java client, http://appium.github.io/java-client/

appium ruby client, http://seleniumcucumber.info/