[JAVA #12] [Web Automation #12] Selenium With TestNG #1 Installing TestNG [KR]

redsjavahouse1591357_1280.jpg
image source: pixabay


오늘부터는 Selenium과 TestNG를 결합해 사용해 본다.
TestNG란 이름에서도 알 수 있듯이 소프트웨어 테스트에 필요한 테스트 프레임워크이다. 다양한 annotation으로 테스트에 필요한 케이스를 정리할 수 있고, 거의 완벽에 가까운 리포트도 제공한다. 개발자들이 자주 사용하는 Junit에서 영감을 받아서 만들어졌다고 하고 TestNG는 QA에 적합하게 설계된 것으로 보인다.

지금까지의 코드는 모두 main() 메서드에 작성했는데 간단한 코드에서는 문제없으나 코드가 복잡해질 경우 main() 메서드 하나만으로 너무 부족하다.
예를 들어 아래와 같은 테스트 케이스가 있다고 가정한다.

a. 브라우저 오픈 
b. 다른 사람 글 불러오기
c. 로그인
d. 글쓰기
e. 브라우저 종료
  • 코드 수정이 쉽지 않다.
    만약 d 케이스만 문제가 있어 수정을 해야 할 경우 b,c 는 주석 처리해야 하는 번거로움이 있고 실수하기도 쉽다.
  • 공통 코드 관리가 부실하다.
    a, e 두 개는 테스트 시작과 끝에 필수로 넣어야 하는 부분으로 모든 케이스에 공통으로 사용되는데 main()메서드 시작과 끝에 강제로 넣으면 여러모로 피곤하다.
  • 테스트 케이스 관리가 어렵다.
    수백, 수천 개의 테스트 케이스를 main() 메소드에 모두 넣어버리면 유지보수가 어려워지고 코드가 복잡해진다.

고로 TestNG와 같은 프레임워크가 필요하다.


그럼 먼저 설치를 해본다. 예전에는 Eclipse Marketplace에서 검색 및 설치 가능했지만, 언제부턴가 안되네? 직접 설치해 보자.

  1. Eclipse 상단 메뉴 > help > Install New Software... > 팝업창 Work with 박스에 http://dl.bintray.com/testng-team/testng-eclipse-release/ 입력 후 엔터 치면 TestNG가 검색결과 노출되고 아래 이미지 참고하여 안내에 따라 설치하면 된다. (아래 사진은 윈도우에서 캡처했고 다른 이미지는 맥에서 캡처했다. 설치 방법이 언제부턴가 변경되어 부득이하게 설치 이미지만 윈도우에서 다시 캡처함)
    image.png

  2. 우측 하단에 설치 프로그레스 바가 생성되면서 TestNG가 설치되고 도중에 아래와 같은 얼럿이 뜨면 Install anyway 클릭, 또 재부팅 얼럿이 뜨면 재부팅 하면 성공.
    image.png

  3. 좌측 프로젝트에서 마우스 우클릭 > Properties > Java Build Path > Libraries > Add Library.. > TestNG 추가하면 좌측 프로젝트에 TestNG 라이브러리가 추가된다. 준비 끝...
    Install2.png

  4. TestNG의 annotation을 사용하여 코드를 작성해보자.
    TestNG에는 여러 annotation이 존재하는데 그 중에서 가장 자주 사용하는 @Test, @BeforeClass, @AfterClass로 우선 작성한다.
    @Test - 하나의 케이스를 의미한다. 예를 들어 로그인, 글쓰기 등이 되겠다.
    @BeforeClass - @Test를 포함한 모든 클래스가 실행되기 전 실행된다. 보통 공통으로 사용되는 setUp메서드를 모아 놓는다.
    @AfterClass - @Test를 포함한 모든 클래스가 실행된 후 실행된다. 보통 브라우저를 종료하는 코드가 포함된다.

위에서 언급한 시나리오를 기존 코드와 TestNG 코드로 비교해 보면 아래와 같다.

AS-IS:
package com.steem.webatuo;

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

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;

import io.github.bonigarcia.wdm.WebDriverManager;

public class Steemit {

    public static void main(String[] args) throws InterruptedException {
        WebDriverManager.chromedriver().setup();
        WebDriver driver = new ChromeDriver();
        driver.get("steemit.com/@june0620/feed");
        driver.manage().window().maximize();
        driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
        
        List<WebElement> list = driver.findElements(By.xpath("//div[@id='posts_list']/ul/li"));
        for(WebElement post : list) {
            String title = post.findElement(By.xpath("descendant::h2/a")).getText();
            String author = post.findElement(By.xpath("descendant::span[@class='user__name']")).getText();
            System.out.println(author + "님의 글: " + title);
        }
//Click the login button
 driver.findElement(By.linkText("로그인")).click();
 // type id
 driver.findElement(By.name("username")).sendKeys("june0620");
 // type pw and submit
 WebElement pw = driver.findElement(By.name("password"));
 pw.sendKeys(Password.getPw());
 pw.submit();
 Thread.sleep(5000);
 // Click the write Button 
 List<WebElement> writeBtns = driver.findElements(By.xpath("//a[@href='/submit.html']"));
 for(WebElement writeBtn : writeBtns) {
 if(writeBtn.isDisplayed()) {
 writeBtn.click();
 Thread.sleep(2000);
 //type Text and Keys
 WebElement editor = driver.findElement(By.xpath("//textarea[@name='body']"));
 String keys = Keys.chord(Keys.LEFT_SHIFT, Keys.ENTER);
 editor.sendKeys("hello!! world", keys, "hello!!! STEEMIT", Keys.ENTER, "안녕, 스팀잇", Keys.ENTER, "你好!似提姆");
 break;
 }
 }
 
 Thread.sleep(5000);
        driver.quit();
    }

}

TO-BE:
package com.steem.webatuo;

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

import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

import io.github.bonigarcia.wdm.WebDriverManager;

public class Steemit {
    WebDriver driver;

    @BeforeClass
    public void SetUp() {
        WebDriverManager.chromedriver().setup();
        driver = new ChromeDriver();
        driver.get("steemit.com/@june0620/feed");
        driver.manage().window().maximize();
        driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
    }

    @Test
    public void CASE_01_GetPosts() {
        List<WebElement> list = driver.findElements(By.xpath("//div[@id='posts_list']/ul/li"));
        for (WebElement post : list) {
            String title = post.findElement(By.xpath("descendant::h2/a")).getText();
            String author = post.findElement(By.xpath("descendant::span[@class='user__name']")).getText();
            System.out.println(author + "님의 글: " + title);
        }
    }

    @Test
    public void CASE_02_Login() throws InterruptedException {
        // Click the login button
        driver.findElement(By.linkText("로그인")).click();
        // type id
        driver.findElement(By.name("username")).sendKeys("june0620");
        // type pw and submit
        WebElement pw = driver.findElement(By.name("password"));
        pw.sendKeys(Password.getPw());
        pw.submit();
        Thread.sleep(5000);
    }

    @Test
    public void CASE_03_Write() throws InterruptedException {
        // Click the write Button
        List<WebElement> writeBtns = driver.findElements(By.xpath("//a[@href='/submit.html']"));
                Assert.assertFalse(writeBtns.isEmpty());
        for (WebElement writeBtn : writeBtns) {
            if (writeBtn.isDisplayed()) {
                writeBtn.click();
                Thread.sleep(2000);
                // type Text and Keys
                WebElement editor = driver.findElement(By.xpath("//textarea[@name='body']"));
                String keys = Keys.chord(Keys.LEFT_SHIFT, Keys.ENTER);
                editor.sendKeys("hello!! world", keys, "hello!!! STEEMIT", Keys.ENTER, "안녕, 스팀잇", Keys.ENTER, "你好!似提姆");
                break;
            }
        }
        Thread.sleep(5000);
    }

    @AfterClass
    public void tearDownClass() {
        driver.quit();
    }
}

보다시피 코드 가독성이 좋아졌고, 유지보수도 쉽게 할 수 있을 것 같다.
만약 CASE_03_Write()에 문제가 생겨 수정이 필요할 경우 나머지 두개 케이스는 @Test만 주석처리 하면 된다.
코드에서 마우스 우클릭 > Run As.. > TestNG Test로 케이스를 실행하면 리포트도 요렇게 예쁘게 노출되는것을 볼수 있다. 👇
run1.png
물론 fail 되었을 경우도 빨간색으로 표시해준다. 👇 코드는 글쓰기 버튼을 찾지 못했고, TestNG에서 지원하는 Asserting 메서드를 통과하지 못해 발생했다.
Asserting은 다음에 알아보도록 한다.
image.png

에러 코드:

List<WebElement> writeBtns = driver.findElements(By.xpath("//Invalid xPath"));
Assert.assertFalse(writeBtns.isEmpty());

이 외에도 TestNG의 annotation은 다양하게 존재하는데 차차 정리할 예정이다.

.
.
.
.
[Cookie 😅]
Seleniun java lib version: 3.141.59
java version: 13.0.1

TestNG 소개 참고
https://en.wikipedia.org/wiki/TestNG
https://testng.org

TestNG 설치 방법 참고 - https://www.guru99.com/install-testng-in-eclipse.html)

TestNG annotation 참고 - https://www.tutorialspoint.com/testng/testng_basic_annotations.htm