지식메모/자동화




UI Automator Viewer 를 사용하여 화면 정보 얻기

UI Automator Viewer 는 Android SDK를 설치하면 같이 설치된다. 경로는 sdk 설치된 폴더에서 sdk/tools 폴더 안에 uiautomatorviewer.bat 파일로 있다.

폰을 연결한 후 viewer를 실행시킨 뒤 왼쪽 상단 device screenshot을 클릭하면 현재화면이 나타난다. 각 구성요소를 선택하면 우측에 정보가 나타난다.


▲uiautomator viewer 실행 후 화면 정보 얻어오는 모습


이제 여기서 얻은 정보를 로보티움에 사용한다.


버튼 클릭하기

Solo.clickOnView(View view) 로 클릭

ex) solo.clickOnView(solo.getView(id)); 버튼의 resourceid를 입력하여 클릭

네이버 앱인 경우 메뉴버튼을 누르고자 할 때


메뉴버튼의 resource-id 는 header_open_slide 인걸 볼 수 있다.

그리하여 solo.clickOnView(solo.getView("header_open_slide"));  이렇게 해주면 메뉴버튼을 누를 수 있다.


텍스트를 찾아 클릭하기

solo.clickOnText(java.util.regex.Pattern.quote(String text));

해당 text를 찾아 클릭한다.

ex) solo.clickOnText(java.util.regex.Pattern.quote("확인")); 확인이 써져있는 버튼을 클릭



그리드뷰 구성에서 항목 선택하기

solo.clickInList(int line); 

그리드뷰로 구성된 화면에서 resource-id가 없는 경우가 있는데 이럴 때 index 번호로 클릭할 수 있었다. 각 영역에는 index가 있었다.



예를 들면 위 사진에서 index 5번 사진을 클릭하고 싶으면 solo.clickInList(6); 을 해주었더니 클릭되었다. index+1 로 값을 넣어주면 선택이 되었다.



레이아웃 index를 사용하여 클릭하기

solo.clickOnImage(int index);

버튼은 있는데 resource-id가 없는 경우가 있었다.  그래서 찾아보니 clickOnImage로 클릭할 수가 있었는데 레이아웃 index를 사용하였다.


위 사진에서 보듯이 버튼에는 id가 없지만 레이아웃 자체에 index가 있었다. solo.clickOnImage(2) 를 해주니 클릭은 되었는데 다른 영역에도 index가 있어서 구분하여 사용하는 방법을 알아봐야할 것 같다.



스크롤 및 슬라이드 하기

solo.drag(float fromX, float toX, float fromY, float toY, int stepCount) 를 이용하여 스크롤 동작 확인

변수 설명

float fromX : 가로좌표 시작점

float toX : 가로좌표 도착점

float fromY : 세로좌표 시작점

float toY : 세로좌쵸 도착점

int stepCount : 드래그 속도(숫자가 작을수록 드래그가 빠름)


상하 스크롤의 경우 X좌표 고정 후 Y좌표로 구현 ex) solo.drag(100,100,50,350,40) 가로는 100,100위치에 고정 세로로 50부터 350까지 드래그

좌우 슬라이드일 경우 반대로 Y좌표 고정 후 X좌표로 구현

단, 해상도에 따른 정확한 좌표를 구하는 방법이 필요하다. 720x1280 기준의 좌표와 400x800 기준의 좌표는 다를 것이다. 절대좌표가 있는지는 모르겠음.


스크롤 내리기

solo.scrollDown();

드래그가 아닌 그냥 스크롤로 화면을 내려준다.



텍스트 찾기

solo.searchText(String text);

text를 찾는다. 반환 값은 boolean 값. ex) boolean text_search = solo.searchText("안녕");


solo.waitForText(String text);

특정 문자열이 나타날 때까지 기다린다. 기본값은 20초 이며 찾으면 true, 못 찾으면 false를 반환


solo.waitForText(String text, int minimumNumberOfMatches, long timeout);

minimumNumberOfMatches : 일치되는 최소 개수

timeout : 대기 시간 설정 (단위 밀리세크 1000ms =1초)


텍스트 입력하기

solo.enterText(int index,String text);

index : 텍스트 입력 창의 index값

text : 입력할 텍스트

ex) solo.enterText( 1, "안녕하세요");


solo.enterText(android.widget.EditText editText, String text);

editText : 텍스트 입력 창의 editText id 값

text : 입력할 텍스트

ex) solo.enterText((EditText)solo.getView("id"), "안녕하세요");


EditText 내 텍스트 삭제

solo.clearEditText(int index); 

index : EditText의 index 


또는 solo.clearEditText(android.widget.EditText editText);
ex) solo.clearEditText((EditText)solo.getView("id"))











필수 환경


자바 설치

자바 path 설정

이클립스 설치

android sdk 설치


위 환경이 설치되어 있다는 가정하에 실행



로보티움 튜토리얼 페이지


https://github.com/RobotiumTech/robotium/wiki/Robotium-Tutorials


위 링크에서

환경 설치부터 예제 소스코드와 실행방법 pdf 파일까지 받을 수 있다.





위와 같은 페이지가 나타나는데 


우선 Calculator Source Code 와 


아래 Black Box Testing 에서 Source Code를 내려받는다.


그런 다음 이클립스에서 2개의 프로젝트를 import 한다음

(Android 프로젝트 Exisiting Android Code Into Workspace로 import)


에뮬레이터를 실행하거나 apk를 설치할 폰을 준비한다.


AndroidCalculator 프로젝트 파일





우선 Android Calculator 프로젝트 우클릭 > Run As > Android Application 을 실행하여 

에뮬레이터나 연결된 폰에 Calculator 앱을 설치한다.



설치가 되었으면 이제 BlackBox Testing 프로젝트를 실행해야 한다.


testAndroidCalculatorBlackBox  프로젝트 모습




위 Calculator와 다른 점은 robotium-solo-5.0.1.jar 라이브러리가 있다는 점이다.

저게 있어야 테스팅이 가능하다.



TestApk.java 파일을 보면



package com.testcalculator;

import com.robotium.solo.Solo;
import android.test.ActivityInstrumentationTestCase2;

@SuppressWarnings("unchecked")
public class TestApk extends ActivityInstrumentationTestCase2 {

	private static final String LAUNCHER_ACTIVITY_FULL_CLASSNAME = "com.calculator.Main"; // 꼭 확인
	private static Class launcherActivityClass;
	static {

		try {
			launcherActivityClass = Class
					.forName(LAUNCHER_ACTIVITY_FULL_CLASSNAME);
		} catch (ClassNotFoundException e) {
			throw new RuntimeException(e);
		}
	}

	public TestApk() throws ClassNotFoundException {
		super(launcherActivityClass);
	}

	private Solo solo;

	@Override
	protected void setUp() throws Exception {
		solo = new Solo(getInstrumentation(), getActivity());
	}

	public void testDisplayBlackBox() {
		// Enter any integer/decimal value for first editfield, we are writing  10
		solo.clearEditText(0);
		solo.enterText(0, "10");
		
		// Enter any integer/decimal value for first editfield, we are writing  20
		solo.clearEditText(1);
		solo.enterText(1, "20");
		
		// Click on Multiply button
		solo.clickOnButton("Multiply");

		// Verify that resultant of 10 x 20
		assertTrue(solo.searchText("200"));
	}

	@Override
	public void tearDown() throws Exception {
		solo.finishOpenedActivities();
	}

}


예제 코드라 알아서 되어 있겠지만 패키지 네임을 꼭 확인한다. 이 패키지 네임은 테스트할 앱 내 클래스 파일명으로 해주어야 한다.


private static final String LAUNCHER_ACTIVITY_FULL_CLASSNAME = "com.calculator.Main";


그래야 해당 앱을 실행해서 테스트가 가능하다.


또 확인해야 할 것이 메니페스트 파일인데 열어보면



<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.testcalculator"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="10" />

    <instrumentation
        android:name="android.test.InstrumentationTestRunner"
        android:targetPackage="com.calculator" android:label="TestAndroidCalculatorBlackBox"/>

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <uses-library android:name="android.test.runner" />
    </application>

</manifest>




<manifest xmlns:android="http://schemas.android.com/apk/res/android"   package="com.testcalculator" // 이 패키지명은 test프로젝트의 패키지 명이어야 하고


<instrumentation

        android:name="android.test.InstrumentationTestRunner"

        android:targetPackage="com.calculator" android:label="TestAndroidCalculatorBlackBox"/>


이 타겟 패키지명은 해당 앱 패키지명이어야 한다. 말 그대로 타겟이니 테스트할 앱 패키지명을 적어준다.


위 프로젝트 사진을 보면 

패키지명이 나와있다.



 AndroidCalculator 패키지명 : com.calculator

testAndroidCalculatorBlackBox 패키지명 : com.testcalculator 

 

 



확인이 끝났다면 

testAndroidCalculatorBlackBox  프로젝트 우클릭 > Run As > Android Juit Test  을 클릭하여 테스트를 실행한다.


물론 에뮬레이터에 앱을 설치헀으면 똑같이 에뮬레이터에서 실행하고 폰이면 폰에서 실행한다.


폰으로 실행한 영상




이렇게 자동으로 곱셈계산이 된다.


마지막으로 요약하자면


1. 로보티움 튜토리얼 페이지에서 AndroidCalculator  프로젝트와 testAndroidCalculatorBlackBox  프로젝트 파일을 내려받는다.


2. 이클립스에서 두 프로젝트를 import 한다.


3. 먼저 AndroidCalculator  을 실행하여 설치한다.


4. testAndroidCalculatorBlackBox  프로젝트 패키지명 확인한 뒤 Run As > Android Juit Test 로 실행한다.


나중에 apk 파일만 가지고 테스트 하는 방법을 알아보아야겠다.


앱 서명문제 때문에 번거로운거 같다..













셀레니움 JAVA 버전 jar 라이브러리 다운로드 후 프로젝트에 추가

http://docs.seleniumhq.org/download/



2.44.0 클릭하여 다운로드



프로젝트에 추가하는 방법

프로젝트 우클릭 > Properties > Java Build Path > Add External JARs.. 클릭 후 해당 라이브러리 선택하여 추가



크롬브라우저 용 드라이버 파일

chromedriver.exe


실행 동영상





소스코드



package work2;

import java.util.List;
import java.util.concurrent.TimeUnit;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.Select;

@FixMethodOrder (MethodSorters.NAME_ASCENDING)

public class INTest {
	private static WebDriver driver;
	String Title = null;
	String URL = null;
	String alertText = "";

	@BeforeClass
	public static void setUp() throws Exception {
		System.setProperty("webdriver.chrome.driver", "C:\\chromedriver.exe"); //크롬 드라이버 파일 경로설정
		driver = new ChromeDriver();
		driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS); //응답시간 5초설정
		driver.get("http://www.naver.com");  //접속할 사이트
	}

	@Test
	public void Step_01_지식인_로그인_Test () throws Exception {
		
		driver.findElement(By.id("svc.kin")).click();    //지식인 클릭
		driver.findElement(By.id("login_btn")).click();  //NAVER로그인 클릭

		driver.findElement(By.id("id")).sendKeys("ID");  //ID
		driver.findElement(By.id("pw")).sendKeys("PASSWORD"); //비번
		driver.findElement(By.className("int_jogin")).click(); //로그인 버튼 클릭

	}
	@Test
	public void Step_02_임시저장_Test () throws Exception {
		
		
		for(String Handle : driver.getWindowHandles())  //새창으로 스위칭
			driver.switchTo().window(Handle);
		driver.findElement(By.xpath("//*[@id='main_top_2']/div[1]/fieldset[1]/div/a/img")).click();//질문하기 클릭
		
		driver.switchTo().frame("editor");  //질문하기 입력창 iframe으로 스위칭
		
		driver.findElement(By.xpath("//*[@id='title']")).sendKeys("원투쓰리포"); //제목입력
		driver.switchTo().frame("SmartEditorIframe"); 						//내용입력 창 iframe 스위칭
		driver.findElement(By.cssSelector("body")).sendKeys("일이삼사");		//내용입력
		
		driver.switchTo().defaultContent();									//스위칭 초기화
		//다시 질문하기 창 iframe 스위칭
		driver.switchTo().frame(driver.findElement(By.xpath("//*[@id='editor']"))); 
		
		
		
		driver.findElement(By.xpath("//*[@id='au_submit_button']/div[2]/a[1]/img")).click();  //임시저장 클릭
		
		String num = driver.findElement(By.id("temporary_save_count")).getText();	//현재 임시저장 글 수 카운트 추출
		
		Thread.sleep(1000);		//너무 빨라 얼럿 창 노출이 안되는 걸 방지하기 위해 슬립 추가
		
		Alert alert = driver.switchTo().alert();			//얼럿 창으로 스위칭
		alertText = alert.getText();						//얼럿 창 메시지 추출
		alert.accept();										//얼럿 창 확인 클릭
		System.out.println(alertText);						//얼랏 창 메시지 출력
		
		Thread.sleep(1000);
		
		System.out.println("임시저장 글 개수 : "+num);
		
		driver.findElement(By.xpath("//*[@id='title']")).clear();		//질문 제목 삭제
		driver.findElement(By.xpath("//*[@id='title']")).sendKeys("파이브식스세븐에잇");	//질문 다시 입력
				
		
		
		driver.switchTo().frame("SmartEditorIframe");					//내용 입력 창 iframe 스위칭
		driver.findElement(By.cssSelector("body")).sendKeys("오육칠팔");	//내용입력
		driver.switchTo().defaultContent();								//창 스위칭 초기화
		//다시 질문하기 창 iframe 스위칭
		driver.switchTo().frame(driver.findElement(By.xpath("//*[@id='editor']")));	
		
		Thread.sleep(1000);
		

	}
	
	@Test
	public void Step_03_다음단계_Test () throws Exception {
		driver.findElement(By.xpath("//img[@alt='다음단계']")).click(); //다음단계 버튼 클릭
		
		//카테고리
		Thread.sleep(1000);
		driver.findElement(By.linkText("디렉토리 직접 선택")).click(); 	//디렉토리 직접선택 클릭
		
		
		Select dropdown = new Select(driver.findElement(By.id("choose_dir_d1")));  //첫번째 셀렉 박스를 가져옴
		dropdown.deselectAll();					//선택 모두 초기화
		dropdown.selectByValue("1");			//value 1인 값을 선택
		
		
		dropdown = new Select(driver.findElement(By.id("choose_dir_d2")));		//두번째 셀렉박스 가져옴
		dropdown.deselectAll();													//이하 동일
		dropdown.selectByValue("101");		
		
		dropdown = new Select(driver.findElement(By.id("choose_dir_d3")));
		dropdown.deselectAll();
		dropdown.selectByValue("10103");
		
		dropdown = new Select(driver.findElement(By.id("choose_dir_d4")));
		dropdown.deselectAll();
		dropdown.selectByValue("1010303");
		
		Thread.sleep(1000);
		
		driver.switchTo().defaultContent();				//창 스위칭 초기화
		//질문하기 창 iframe 스위칭
		driver.switchTo().frame(driver.findElement(By.xpath("//*[@id='editor']")));	
		driver.findElement(By.xpath("//*[@id='au_submit_button2']/div[2]/a[1]/img")).click();//임시저장 클릭
		
		Thread.sleep(3000);
		
		Alert alert = driver.switchTo().alert();		//얼럿 창 스위칭
		alertText = alert.getText();
		alert.accept();									//얼럿 확인 클릭
		System.out.println("다른제목으로 "+alertText);
		
		String num = driver.findElement(By.id("temporary_save_count")).getText();  //임시저장 카운트 추출
		System.out.println("임시저장 글 개수 : "+num);
		
		
		

	}
	
	@Test
	public void Step_04_이전_글_임시저장_삭제_Test () throws Exception {
		
		// 임시저장 창
		driver.findElement(By.className("_tempsave_open_close")).click();   //임시저장 글 클릭

		driver.switchTo().defaultContent();									//창 초기화
		driver.switchTo().frame(driver.findElement(By.xpath("//*[@id='editor']"))); //질문하기 iframe 창 스위칭

		String info = driver.findElement(By.className("info_tempsave")).getText(); //임시저장 창 안내 글 추출
		System.out.println("임시저장 창 " + info);

		WebElement tempList = driver.findElement(By.className("tempsave_list")); //임시저장 글 목록 가져옴
		List<WebElement> list = tempList.findElements(By.className("q_on"));   	//목록을 리스트에 넣어둠

		//이전 글이니까 2번째인 1번 목록 삭제 버튼을 클릭
		list.get(1).findElement(By.tagName("img")).click();
		Thread.sleep(3000);
		Alert alert = driver.switchTo().alert();			//삭제 확인 얼럿 창
		alertText = alert.getText();
		alert.accept();										//확인 클릭
		System.out.println("이전" + alertText);

		
		
	}
	
	@Test
	public void Step_05_임시저장_삭제_Test () throws Exception {
		/*******이전 글 삭제와 동일*******/
		driver.switchTo().defaultContent();								
		driver.switchTo().frame(driver.findElement(By.xpath("//*[@id='editor']")));

		WebElement tempList1 = driver.findElement(By.className("tempsave_list"));
		List<WebElement> list1 = tempList1.findElements(By.className("q_on"));

		//여기서 현재글 삭제를 위해 1번째인 0번째 목록 삭제버튼 클릭
		list1.get(0).findElement(By.tagName("img")).click();	
		Thread.sleep(3000);
		Alert alert = driver.switchTo().alert();
		alertText = alert.getText();
		alert.accept();
		System.out.println(alertText);

		Thread.sleep(1000);

		String num = driver.findElement(By.id("temporary_save_count")).getText();
		System.out.println("임시저장 글 개수 : " + num);

		Thread.sleep(1000);
		
		
	}
	
	

	@AfterClass
	public static void tearDown() throws Exception {
		driver.quit();
	}

}




+ Recent posts