지식메모/자동화




Naver Open API가 방식이 바뀌어서 이전 글에서 했던 방식은 동작이 되지않는다.

하지만 사전 준비는 동일하니 아래 글을 참고하면 된다.


REST-Assured 를 사용하여 maven 프로젝트로 간단한 API 테스트

http://yonoo88.tistory.com/886


우선 네이버오픈API 페이지에 들어간다.

https://developers.naver.com/main/

들어가서 상단 Application > 애플리케이션 등록을 들어간다.


애플리케이션 이름적고 사용할 API 추가하고 환경은 저렇게 해주어도 된다.



등록하기를 누르면 아래와 같은 화면이 나오는데 

Client ID 와 Client Secret 저 2개가 필요하다.

Client Secret는 우측 보기를 누르면 표시된다.

이 2개를 복사해서 코드에 넣어주어야 한다.

저 2개 값을 header 값으로 추가해주어야 한다.

String 값으로 Client ID,와 Client Secret 에 개인 값을 넣어준다.

그럼 api가 정상적으로 동작한다.



예제코드 뉴스 검색 API 사용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package NaverAPITest.NAPITest;
 
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
 
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
 
import org.junit.Before;
import org.junit.Test;
 
public class test {
 
    @Before
    public void apiTest() {
         baseURI = "http://openapi.naver.com";
//        baseURI = "http://maps.googleapis.com";
        port = 80;
    }
 
    @Test
    public void test() {
        try {
            String text = URLEncoder.encode("최순실""UTF-8");
            given().
              header("X-Naver-Client-Id""Client ID").
              header("X-Naver-Client-Secret","Client Secret").
             params( //URL 주소의 파라미터 값을 넣어줌
             "query",text     
             ).when().
            
             /*baseURL 을 제외한 뒷부분 주소를 get하고
             *body에서 해당 title 내용을 가져올 위치 지정 후
             *확인할 값을 equalTo에 넣어 확인*/
             get("/v1/search/news.xml").then().body("rss.channel.title", equalTo("Naver Open API - news ::'최순실'"));
//             get("/v1/search/news.xml").then().toString();
            
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
    }
 
}
 
cs







버거킹 영수증을 받아보면 하단에 설문조사코드가 있습니다.


설문을 마치면 단품으로 세트를 먹을 수 있는 코드를 발급받게 되는데요


설문조사를 하는 것도 은근히 시간이 걸리는 것 같아 한번 시도해보았습니다.


Selenium 을 사용하였고 스윙으로 UI를 대충 만들어보았습니다.


설문이 끝나면 발급코드가 표시되는 간단한 프로그램입니다.


자바 환경 설치가 필수로 되어 있어야 하며


C: 또는 D: 드라이브 경로에 

chromedriver.exe가 존재해야 합니다.


chromedriver.exe






시연 영상



http://youtu.be/NtjBOtUwknM



셀레니움 부분 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package com.burger.king;
 
import java.io.File;
import java.util.NoSuchElementException;
import java.util.concurrent.TimeUnit;
 
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import static com.codeborne.selenide.Selenide.*;
import static com.codeborne.selenide.Condition.*;
 
@FixMethodOrder (MethodSorters.NAME_ASCENDING)
 
public class survey {
    private static WebDriver driver;
    
    static String receiptNum="";
    static String url="https://kor.tellburgerking.com";
    
    
    public void inputNumber(String number){
          // 텍스트 필드값 가져오기
        receiptNum = number;
    }
 
 
    @BeforeClass
    public static void setUp() throws Exception {
        
        File file = new File("c:/chromedriver.exe");
        File file2 = new File("d:/chromedriver.exe");
           if(file.isFile()){         
               System.setProperty("webdriver.chrome.driver""c:/chromedriver.exe"); //크롬 드라이버 파일 경로설정
        }else if(file2.isFile()){
            System.setProperty("webdriver.chrome.driver""d:/chromedriver.exe"); //크롬 드라이버 파일 경로설정    
        }else{
            //안내문
            runner.labelIntroduce.setText("C 또는 D드라이브에 chromedriver가 없습니다.\n");
        }
        driver = new ChromeDriver();
        driver.manage().timeouts().implicitlyWait(0, TimeUnit.SECONDS); //응답시간 5초설정
        driver.get(url); 
        
         driver.manage().window().maximize();
    }
    
    public static void login() throws InterruptedException   {
        boolean boo = true;
        WebElement NextButton = driver.findElement(By.id("NextButton"));
        NextButton.click();   
        
        driver.findElement(By.id("CN1")).sendKeys(receiptNum);  //ID
        Thread.sleep(1000);
        driver.findElement(By.id("NextButton")).click();
        
        
        int i = 0;
        while(boo){
            try{
                
                boolean radioButtonHolder = driver.findElements(By.className("radioButtonHolder")).size() > 0;
                boolean checkboxBranded = driver.findElements(By.className("checkboxBranded")).size() > 0;
                boolean radioBranded = driver.findElements(By.className("radioBranded")).size() > 0;
                boolean checkCode = driver.findElements(By.className("ValCode")).size() > 0;
                boolean Next = driver.findElements(By.id("NextButton")).size() > 0;
                 if(radioButtonHolder){
                     driver.findElement(By.className("radioButtonHolder")).click();
                 }
                 else if(checkboxBranded){
                     driver.findElement(By.className("checkboxBranded")).click();
                 }
                 else if(radioBranded){
                     driver.findElement(By.className("radioBranded")).click();
                 }
                 if(Next)
                     driver.findElement(By.id("NextButton")).click();
                 if(checkCode)
                     boo = false;
                 System.out.println(i++);
            }catch (NoSuchElementException e){
                
            }
           
        }
        
        String checkCode = driver.findElement(By.xpath("//*[@id='FNSfinishText']/div/p[2]")).getText();
 
        runner.labelIntroduce.setText(checkCode);
    }
    
 
    @Test
    public static void run() throws Exception {
        login();
        Thread.sleep(500);
    }
 
    @AfterClass
    public static void tearDown() throws Exception {
        driver.quit();
    }
 
}
cs



스윙UI 부분


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package com.burger.king;
 
import javax.swing.*;
 
import com.burger.king.runner;
 
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.URL;
 
class runner extends Thread{
    Simulator simul = new Simulator();
    public static    JLabel labelIntroduce;
    public static survey survey = new survey();
    public  static  JTextField tf_receiptNum;
    public static JLabel submit,number;
    public boolean check = true
    public void run(){
        // 텍스트 필드값 가져오기
        String receiptNum = tf_receiptNum.getText();
        try {
            survey.setUp();
        } catch (Exception e2) {
            // TODO Auto-generated catch block
            e2.printStackTrace();
        }
        survey.inputNumber(receiptNum);
        try {
            survey.run();
        } catch (Exception e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
    }
    
}
 
class JPanel033 extends JPanel{        
    runner thread = new runner();
    
    // 클래스 멤버 필드 설정
    private    JLabel name;
    private    JLabel id;
            
    
    private    JButton buttonSave;
    private    JButton buttonStop;
    public boolean check = true
    
    public JPanel033() {         
        
        setLayout(null);
        
        // 라벨
        name = new JLabel("by. yonoo");
        name.setSize(10020);   
        name.setLocation(20);
        
        id = new JLabel("설문조사코드: ");
        id.setBounds(10,30,100,20);
        
        // 텍스트 필드
        runner.tf_receiptNum = new JTextField();             
        runner.tf_receiptNum.setBounds(100,30,200,20);
        
        thread.labelIntroduce = new JLabel("=");
        thread.labelIntroduce.setBounds(10,50,280,20);
       
            
        // 버튼        
        buttonSave = new JButton("시작");
        buttonSave.setBounds(80,80,100,20);
        buttonSave.addActionListener(new EventHandlerSave());   
        
        buttonStop = new JButton("정지");
        buttonStop.setBounds(220,80,100,20);
        buttonStop.addActionListener(new EventHandlerStop());   
        
        add(name);
        add(thread.labelIntroduce);
        
        add(id);
        add(thread.tf_receiptNum);
        
        add(buttonSave);
        add(buttonStop);
        
    }
    
    class EventHandlerSave implements ActionListener{     // 
        public void actionPerformed(ActionEvent e){
//            try {
//                thread.start();    
//            } catch (Exception e1) {
//                // TODO Auto-generated catch block
//                e1.printStackTrace();
//            }
            
            if(check){
                thread.start();    
            }else{
                runner thread = new runner();
                thread.start();    
                check = true;
            }
        }
    }   
    class EventHandlerStop implements ActionListener{     // 
        public void actionPerformed(ActionEvent e){
            check = false;
            System.out.println(check);
            thread.interrupt();
            System.out.println("정지!");
//            System.exit(1);
            
        }
    } 
    
}
 
 
public class Simulator extends JFrame{
    
    public JPanel033 jpanel03 = null;
   
    public static void main(String[] args) {
        Simulator win = new Simulator();
        
        win.setTitle("BurgerKing Survey Automation");
        win.jpanel03 = new JPanel033();
        
        URL imageURL = Simulator.class.getClassLoader().getResource("burger.png");
        System.out.println(imageURL);
        ImageIcon img = new ImageIcon(imageURL);
        win.setIconImage(img.getImage());
        
        win.add(win.jpanel03);
 
        win.setSize(400,150);
        win.setVisible(true);
        win.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        
        
    }
    
}
 
cs





eclipse 에서 maven 프로젝트를 새로 생성한다.







create a simple.. 체크해주었다





이름을 짓고 피니쉬







src/test/java 에 새 패키지를 추가했다.





이런 식으로..






거기에 Junit Test Case 를 생성한다.










그리고 maven 프로젝트의 pom.xml에 다음과 같은 내용을 추가해준다.


https://github.com/rest-assured/rest-assured/wiki/GettingStarted


위 링크로 가면 자세히 나와있다.

우선 그냥 


REST Assured

JsonPath

XmlPath

JSON Schema Validation


4가지를 추가해줬다.






4가지 추가된 모습









예제로 네이버 open api 에서 해보았다.


http://developer.naver.com/wiki/pages/SrchBook


위 링크에 들어가면 예시 URL과 함께 파라미터 값도 나와있다


1. 요청 URL (request url)#

http://openapi.naver.com/search

2. 요청 변수 (request parameter)#

2.1 기본검색#

요청 변수설명
keystring (필수)이용 등록을 통해 받은 key 스트링을 입력합니다.
targetstring (필수) : book서비스를 위해서는 무조건 지정해야 합니다.
querystring (필수)검색을 원하는 질의, UTF-8 인코딩 입니다.
displayinteger : 기본값 10, 최대 100검색결과 출력건수를 지정합니다. 최대 100 까지 가능합니다.
startinteger : 기본값 1, 최대 1000검색의 시작위치를 지정할 수 있습니다. 최대 1000 까지 가능합니다.
  • 샘플 URL ('삼국지'를 검색할 경우)

http://openapi.naver.com/search?key=c1b406b32dbbbbeee5f2a36ddc14067f&query=%EC%82%BC%EA%B5%AD%EC%A7%80&display=10&start=1&target=book



위 링크의 예제 URL로 들어가보면


http://openapi.naver.com/search?key=c1b406b32dbbbbeee5f2a36ddc14067f&query=삼국지&display=10&start=1&target=book



아래와 같은 페이지가 노출되는데

여기 xml값 중 title 내용으로 확인을 해보았다.









아래와 같이 끄적끄적 한 뒤


given().when() 이런 것들은 아직 잘 모름

예시대로 해본 것 뿐 





예시코드


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package rest.test.api.com;
 
import static org.junit.Assert.fail;
 
import org.junit.Before;
 
import static io.restassured.RestAssured.*;
import static io.restassured.matcher.RestAssuredMatchers.*;
import static org.hamcrest.Matchers.*;
import static io.restassured.module.jsv.JsonSchemaValidator.*;
 
import org.junit.Test;
 
public class APITest {
 
    public APITest() {
        baseURI = "http://openapi.naver.com";
        port = 80;
    }
 
    @Test
    public void test() {
        given().
                params( //URL 주소의 파라미터 값을 넣어줌
                "key","c1b406b32dbbbbeee5f2a36ddc14067f",
                "query","삼국지",
                "display","10",
                "start","1",
                "target""book"
                ).when().
                
                /*baseURL 을 제외한 뒷부분 주소를 get하고
                 *body에서 해당 title 내용을 가져올 위치 지정 후
                 *확인할 값을 equalTo에 넣어 확인*/
        get("/search").then().body("rss.channel.title", equalTo("Naver Open API - book ::'sample'"));
    }
 
}
 
cs



body에서 rss.channel.title 인 이유는 페이지에 보면 
rss
ㄴchannel
  ㄴtitle

이런 식으로 구성되어서 그렇다.



equalTo 는 title 을 적어주어 URL을 호출했을 때 equalTo 값과 일치하는지 확인하는 부분이다.



값이 일치한다면 아래처럼 정상적으로 테스트가 초록색으로 끝난다.










번역기를 재가공한 발 번역 주의...


출처 : http://www.softwaretestinghelp.com/mistakes-of-my-life-as-software-tester/?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+Softwaretestinghelp+%28softwaretestinghelp%29


4 Mistakes of My Life as a Software Tester (and You’re Probably Making These)


이 글은 필자가 소프트웨어 테스터로 지내오면서 느낀 4가지 실수를 공유하고 싶어하여 쓴 글입니다. (그리고 아마도 당신이 겪었을 수도 있는..)

 

많은 사람들은 개구리들이 세상 밖으로 나와 어떻게 세계가 이렇게 크고 아름다고 다른지 깨달은 우물 안 개구리 이야기를 알고 있습니다.

당신이 일하고 있는 삶에서 이러한 이야기의 어떤 점에 대해서 생각해보았나요? 현실세계에 온 걸 환영해요

오늘 제가 소프트웨어 테스팅 커리어를 시작하고 나서 겪었던 4가지 실수를 공유하려고 합니다.

 

테스터가 하게되는 흔한 4가지 실수

당신의 커리어에서 다음 사례에 대해 얼마나 많은 생각을 해보았습니까?

 

#1.질문은 항상 필요하지 않다.


우리 모두는 약점을 가지고 있습니다. 인기있는 조사에 따르면 성인들이 가지는 대부분의 약점들은 바보처럼 보이는 것에 대한 두려움입니다. 비현실적인 두려움은 우리의 성장을 막습니다. 가정하면 질문하는 것은 우리가 어떤 것을 배우는 것입니다.


온라인 티켓 예약 어플리케이션을 테스팅 하는 동안 테스터는 사용자가 예약한지 최소 24시간이 지난 티켓은 취소할 수 없다는 것을 발견했었습니다. 지금은 마지막 사용자의 관점에서 이것은 받아들일 수 없는 것입니다. 질문 대신에 행동으로 테스터는 그것이 요구될 수 있는 것으로 가정했었습니다. 이러한 잘못된 가정은 어플리케이션이 시장에서 실패하도록 만들었습니다.


그의 질문 스타일을 잘 알고 있는 한 테스터 친구는 버그가 어떻게 해결되었는지 물어보기 위해 버그 추적 시스템에서 로직을 코딩에 이르기까지 모든 것에 대해 질문하는 것을 보고 그가 웃었다고 말했었지만 그것은 그에게 유익했었어요. 왜냐하면 그는 어떤 것에 대해 명확하고 자신감있게 개발하고 있었기 때문이죠


질문을 주저하지 말고, 당신의 의견을 제시하세요. 테스터로서 당신은 어플리케이션의 동작과 실시간 사용량 데이터에 대한 질문할 모든 권한을 가지고 있습니다.

 

# 2. 자동화를 배우는 것은 어렵고 많은 시간이 소요됩니다.


"자동화" 아직까지도 많은 테스터들에게는 힘든 단어입니다.

아직도 많은 사람들은 이렇게 생각할까요?


자동화를 배우는 것은 시간이 걸릴 수 있습니다

자동화 배우는 것은 어렵습니다

자동화는 의미가 없습니다


현실적이지 않은 변화의 두려움, 새로운 것을 배우는 것에 대한 두려움, 당신의 안주하고 있는 곳에서 벗어나는 것에 대한 두려움, 이것은 아무 것도 아닙니다.

난 당신이 이 까다로운 분야에서 커리어를 쌓고 빠르게 성장하고 싶다면 자동화를 계속 배워나가라고 해주고 싶습니다.


# 3. 문서화 된 테스트 시나리오는 모든 것이 포함되어 있으며, 내가 그 이상을 생각할 필요는 없습니다.


지금까지 트렌드는 요구사항 탐색, 브레인 스토밍, 테스트 시나리오 문서, 기능적인 이해들을 검토하기 위해 보냅니다. 리뷰가 완료되면 테스트 담당자는 테스트 시나리오 문서를 따릅니다. 실시간 탐색적 테스트는 브레인 스토밍이 문서에서 이미 수행되어 필요없는 것으로 생각됩니다.

이것은 완전히 잘못된 접근입니다. 


예를 들면

당신은 그림을 10,30,60 분 동안 계속보고 있습니다. 첫째로, 당신은 그림에서 좋은 점을 찾았지만, 그림에 장시간 노출되어 당신은 결함을 깨닫기 시작합니다. 60 분 동안 그림을 응시 한 후, 당신은 오랫동안 그림을 그리고 그림에 대해 확실히 모든 결함을 알고 있는 것처럼 느낍니다. 그렇게 하루를 보냅니다.


다음 날 다시 그림을보세요. 당신은 어제 구석에서 혼합된 색상을 알아챘습니까? 괜찮아 보였나요? 당신은 잘못된 색상의 혼합물은 실제로 그림의 전체적인 효과를 망치고 있다고 생각하지 않나요? 당신은 어제 그것을 알아차리지 못해서 놀랬습니까? 일상은 우리에게 새로운 시각과 새로운 뷰를 가져다 주고 이로 인해 우리는 그것들은 다르게 보게 됩니다.


저는 테스트 하는 동안 테스트 케이스 문서에 의지하지 않고 본인의 관점을 명확하게 하길 바랍니다.

어플리케이션의 동작을 기록하고 자발적으로 어떤 것을 해보려 노력하세요

 

# 4. 나는 패턴 분석을 하지 않고 버그를 찾기 위해 여기에 있습니다


우리는 언제나 우리의 비지니스는 버그를 찾는 것이라고 확신했고 우리의 마인드라고 가르쳤습니다. 그 외 어떤 것도 우리 책임 범위가 아닙니다.

내가 수년 동안 사용해왔던 좋은 예시로 오늘 날 사고를 바꿔봅시다.


새로 오픈을 하고 열심히 노력해봐도 고객이 없는 레스토랑이 있습니다. 그들은 상황을 분석하는 전문가로 불립니다. 분석가는 레스토랑이 다양하고 분위기,가격 등을 갖췄음에도 단골이 없다는 것을 보았습니다. 그는 한번 다녀간 고객들과 연락하여 그들의 피드백을 받은 결과 그는 고객들이 다양한 음식의 맛을 좋아하지 않는 다는 것을 알았습니다. 즉시 새 요리사와 경험있는 요리사를 고용하자 레스토랑은 지금 줄 서있는 고객들을 볼 수 있습니다.


테스터로서 우리의 역할은 위의 상황에서 분석가와 동일합니다. 우리는 무엇이 잘못되었는지를 지적 할 필요는 없지만, 우리는 어플리케이션에 있는 버그의 어떤 패턴이 있는지와 이런 버그 때문에 다른 부분에 영향을 미칠 수 있는지 분석할 필요가 있습니다.

경험이 있다면 당신은 단지 제한적인 테스팅보다 자세한 분석을 제공할 수 있을 것입니다.


결론:


항상 자신을 업데이트하고 계속 새로운 것과 요구되는 스킬들을 배우고 정보와 문제들,지식을 나누며, 언제나 더 좋은 품질을 요구하는데 할 수 있는 사람이 되는 것을 주저하지 마세요




구현해보려고 한 swipe 영역



위 움짤 처럼 폴더 별 우측 슬라이드 메뉴를 제어해보려고 함






우선 우측 슬라이드 메뉴 버튼의 요소를 파악해봄


위치를 파악할 수 있는 정보가 index정보 밖에 없는 걸로 판단

index로 특정 위치를 지정해보기로 함



그래서 xpath로 버튼 위치를 가져와봄


String xpath = "//android.widget.RelativeLayout[contains(@resource-id,'item') and @index='0']" 

 + "//android.widget.ImageView[contains(@resource-id,'open_edit_menu')]";


부가설명


RelativeLayout의 0번째 idex내

//android.widget.RelativeLayout[contains(@resource-id,'item') and @index='0']


open_edit_menu id를 가진 ImageView 버튼을 가져옴

//android.widget.ImageView[contains(@resource-id,'open_edit_menu')]




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
    //  특정위치에 있는 요소의 xpath 값
        String xpath = "//android.widget.RelativeLayout[contains(@resource-id,'item') and @index='0']"
                + "//android.widget.ImageView[contains(@resource-id,'open_edit_menu')]";
        
        //해당 위치의 슬라이드 메뉴 왼쪽으로 swipe
        driver.swipeSlide(xpath);
 
    /**
     * 슬라이드 메뉴 swipe 하기
     * @param xpath : 특정 슬라이드 메뉴의 위치 xpath
     * @throws Exception - Exception
     */
    public void swipeSlide (String xpath) throws Exception {
        
        WebElement webElement = driver.findElement(By.xpath(xpath));

        int starty = (int) (webElement.getLocation().getY());
        int startx = (int) (webElement.getLocation().getX());

        webElement.click();
        
        driver.swipe(startx+(int)(width*0.1), starty, startx-(int)(width*0.15), starty, 0);
    }
cs




이제 xpath 값을 swipeSlide 메소드에 넣어 버튼으르 클릭한 뒤

버튼의 위치를 추출해낸다.


        int starty = (int) (webElement.getLocation().getY());
        int startx = (int) (webElement.getLocation().getX());


 y와 x좌표를 얻어온뒤 swipe 메소드에 값을 넣어줌

swipe 메소드는 다음과 같이 되어있다


void io.appium.java_client.AppiumDriver.swipe(int startx, int starty, int endx, int endy, int duration)

우측 startx+(int)(width*0.1) 지점에서 좌측 startx-(int)(width*0.15) 까지 swipe

(*width는 동작 초기에 폰 가로사이즈를 가져온 값)


반대 방향으로 swipe하고자 한다면 이 두값을 바꿔주면 된다.


동작시켜보면


swipe가 된다.


만약 xpath에서


//android.widget.RelativeLayout[contains(@resource-id,'item') and @index='2']

로 바꿔주면 3번째 슬라이드 메뉴가 오픈된다.


index 2 일 때 동작



3번째 폴더가 열리게 된다.




  1. 2016.05.27 16:16

    비밀댓글입니다


swipe를 이용한 상하좌우 스크롤 및 스크롤 새로고침 구현

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
 
import io.appium.java_client.AppiumDriver;
import io.appium.java_client.TouchAction;
import io.appium.java_client.android.AndroidDriver;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.NoSuchElementException;
 
import org.openqa.selenium.By;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.DesiredCapabilities;
//import org.junit.Test;
import org.testng.annotations.Test;
 
 
public class Utilities extends AndroidDriver<WebElement> implements TakesScreenshot {
    Dimension size  =  manage().window().getSize();
 
    /**
     * 위로 스크롤
     */
    public void scrollUp() throws Exception {
        Thread.sleep(2000);
        
        int starty = (int) (size.height * 0.80);
        int endy = (int) (size.height * 0.20);
        int startx = size.width / 2;
        System.out.println("startx = " + startx + " ,endy = " + endy + " , starty = " + starty); 
        
        //올리기
        this.swipe(startx, endy, startx, starty, 0);
        Thread.sleep(2000);
    }
    
    /**
     * 아래로 스크롤
     */
    public void scrollDown() throws Exception {
        Thread.sleep(2000);
        int starty = (int) (size.height * 0.80);
        int endy = (int) (size.height * 0.20);
        int startx = size.width / 2;
//        System.out.println("startx = " + startx + " ,endy = " + endy + " , starty = " + starty); 
        
        //내려가기
        swipe(startx, starty, startx, endy, 0);
        Thread.sleep(2000);
    }
    
    /**
     * 스크롤 새로고침
     */
    public void pullToRefresh() throws Exception {
        Thread.sleep(1000);
        int starty = (int) (size.height * 0.5);
        int endy = (int) (size.height * 0.9);
        int startx = (int) (size.width*0.01);
//        System.out.println("startx = " + startx + " ,endy = " + endy + " , starty = " + starty); 
        
        //내려가기
        swipe(startx, starty, startx, endy, 0);
        Thread.sleep(2000);
    }
 
 
    /**
     * 오른쪽에서 왼쪽으로 Swipe
     */
    public void swipeToLeft() throws Exception {
        Thread.sleep(2000);
        int starty = (int) (size.width * 0.80);
        int endx = (int) (size.width * 0.20);
        int startx = size.height / 2;
        
        swipe(startx, starty, endx, starty, 0);
        Thread.sleep(2000);
    }
    
    /**
     * 왼쪽에서 오른쪽으로 Swipe
     */
    public void swipeToRight() throws Exception {
        Thread.sleep(2000);
        int starty = (int) (size.width * 0.80);
        int endx = (int) (size.width * 0.20);
        int startx = size.height / 2;
 
        swipe(endx, starty, startx, starty, 0);
        Thread.sleep(2000);
    }
        
    
}
 
 
cs





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

티스토리 툴바