728x90
반응형
SMALL

Appium을 이용해서 App Automation Testing을 할 때 가장 큰 난관 중 하나는 WebView에서 요소들을 가져올 수 없을 때다.

Appium Inspector 도구를 사용해도 WebView에서 필요한 요소들을 가져올 수 없을 때가 종종 있는데 이걸 Visual Testing으로 극복해보자. 

 

OpenCV 설치

우선, 이미지 처리를 위해 거의 모든걸 다 가지고 있는 OpenCV가 필요하다.

 

우선 환경설정을 해줘야한다. (MacOS)

export OPENCV4NODEJS_DISABLE_AUTOBUILD=1

 

나는 MacOS 유저이기 때문에 다음과 같이 Homebrew를 이용해 설치한다.

brew update
brew install opencv@4
brew link --force opencv@4

 

이 두가지 작업을 모두 다 하면 Appium이 OpenCV에 접근할 수 있도록 해주어야 한다.

Global node_modules 경로를 환경설정에 추가해주자. (MacOS)

export NODE_PATH=/usr/local/lib/node_modules

 

그리고 Appium을 설치했다면 저 node_modules 경로에 가면 appium 폴더가 있을텐데 그 폴더안에 들어가서 다음 명령어를 수행

npm i

 

이렇게 하면 OpenCV 설치와 Appium이 OpenCV에 접근 가능하도록 설정을 한 상태이다.

 

Appium Plugin 설치

이제 이미지 처리를 하기 위해 Appium의 플러그인을 설치해줘야 한다.

appium plugin install images

 

설치가 완료되면 이 플러그인을 사용하면서 Appium 서버를 실행해야 한다. 그래서 앞으로 Appium 서버를 실행할 땐 다음 명령어로 실행한다. 

appium --use-plugins images

 

 

이제 테스트 코드를 작성해보자.

테스트 코드 작성하기

우선 테스트 코드를 작성할 파일 경로는 다음과 같다.

src/test/java/AppiumSampleTest.java

 

AppiumSampleTest

@Slf4j
public class AppiumSampleTest {

    public static AndroidDriver driver;
    private static final double VISUAL_THRESHOLD = 0.99;

    @BeforeAll
    public static void setUp() throws MalformedURLException {
        UiAutomator2Options options = new UiAutomator2Options()
                .setUdid("HVA1FG23").setAutoGrantPermissions(true);

        URL appiumServer = URI
                .create("http://0.0.0.0:4723")
                .toURL();
        driver = new AndroidDriver(appiumServer, options);
    }

    @After
    public void tearDown() {
        if (driver != null) {
            driver.quit();
        }
    }
}

 

천천히 하나씩 해보자.

우선, 드라이버를 초기화해줘야한다. 그래서 모든 테스트마다 다 테스트 하기 전 드라이버를 초기화해야하니 @BeforeAll 애노테이션을 붙인 setUp()을 작성하자. 

 

그 안에서 하는 작업은 간단하다. 드라이버 초기화 시 옵션(UDID 설정, AutoGrantPermissions 허용)과 Appium Server를 드라이버에 넣어주면 된다. (여기서 옵션에 APK 경로 지정을 해서 시작하면서 해당 앱을 띄우게 해도 되는데 나는 테스트용이라 그냥 앱이 띄워져있다는 가정하에 실행하는거라 따로 옵션을 주지 않았다)

 

@After 애노테이션이 붙은 tearDown() 안에서는 드라이버를 quit()한다.

 

그리고 이 THRESHOLD 이 부분을 보자.

private static final double VISUAL_THRESHOLD = 0.99;

이미지의 유사도를 비교할 것이기 때문에 99퍼센트의 임계점에 대한 상수값이다.

 

 

이제 실제 테스트를 작성한다.

@Test
public void visualSimilarityMatching() throws IOException {

    log.info("System path = {} ", System.getProperty("user.dir"));

    File baseImage = new File("./src/test/resources/assets/base.png");

    if (!baseImage.exists()) {
        File screenshot = driver.getScreenshotAs(OutputType.FILE);
        FileUtils.copyFile(screenshot, new File("./src/test/resources/assets/base.png"));
    }

    SimilarityMatchingOptions opts =
            new SimilarityMatchingOptions().withEnabledVisualization();

    SimilarityMatchingResult res =
            driver.getImagesSimilarity(baseImage, driver.getScreenshotAs(OutputType.FILE), opts);

    if (res.getScore() >= VISUAL_THRESHOLD) {
        log.info("유사도 = [{}], 동일 화면임", res.getScore());
    } else {
        fail("유사도 = " + res.getScore() + ", 동일 화면이 아님");
    }
}

 

우선, 비교할 파일을 먼저 구비를 해둬야한다. 나의 경우 이런 이미지를 미리 src/test/resources/assets/base.png 경로에 넣어두었다.

 

그래서 이 파일이 저 경로에 없는 경우에 현재 드라이버와 연결된 앱의 화면을 스크린샷을 찍고 아닌 경우 넘어간다. 그 부분이 다음 코드.

File baseImage = new File("./src/test/resources/assets/base.png");

if (!baseImage.exists()) {
    File screenshot = driver.getScreenshotAs(OutputType.FILE);
    FileUtils.copyFile(screenshot, new File("./src/test/resources/assets/base.png"));
}

 

이제 Appium에서 제공하는 SimilarityMatchingOptions를 가져다가 사용해야 한다. 다음 코드.

SimilarityMatchingOptions opts =
                new SimilarityMatchingOptions().withEnabledVisualization();

SimilarityMatchingResult res =
        driver.getImagesSimilarity(baseImage, driver.getScreenshotAs(OutputType.FILE), opts);

 

그래서 이미지 두 개를 비교한다.

SimilarityMatchingResult res = driver.getImagesSimilarity(baseImage, driver.getScreenshotAs(OutputType.FILE), opts);
  • baseImage: 비교할 이미지
  • driver.getScreenshotAs(OutputType.FILE): 현재 드라이버를 통해 스크린샷을 찍으면 나오는 이미지

이 두 이미지를 비교한 결과가 res에 담긴다.

이제 이 코드를 테스트 해보자. 현재 내 Real Device는 딱 이 상태로 보여진다. 그러니까 아까 이미지(baseImage)랑 시스템 바 빼고 다 똑같다.

 

 

최종 코드 (AppiumSampleTest)

import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.options.UiAutomator2Options;
import io.appium.java_client.imagecomparison.OccurrenceMatchingOptions;
import io.appium.java_client.imagecomparison.OccurrenceMatchingResult;
import io.appium.java_client.imagecomparison.SimilarityMatchingOptions;
import io.appium.java_client.imagecomparison.SimilarityMatchingResult;
import lombok.extern.slf4j.Slf4j;
import net.sourceforge.tess4j.Tesseract;
import net.sourceforge.tess4j.TesseractException;
import org.apache.commons.io.FileUtils;
import org.junit.After;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.OutputType;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;

import static org.junit.jupiter.api.Assertions.*;

@Slf4j
public class AppiumSampleTest {

    public static AndroidDriver driver;
    private static final double VISUAL_THRESHOLD = 0.99;

    @BeforeAll
    public static void setUp() throws MalformedURLException {
        UiAutomator2Options options = new UiAutomator2Options()
                .setUdid("HVA1FG23").setAutoGrantPermissions(true);

        URL appiumServer = URI
                .create("http://0.0.0.0:4723")
                .toURL();
        driver = new AndroidDriver(appiumServer, options);
    }

    @After
    public void tearDown() {
        if (driver != null) {
            driver.quit();
        }
    }

    @Test
    public void visualSimilarityMatching() throws IOException {

        log.info("System path = {} ", System.getProperty("user.dir"));

        File baseImage = new File("./src/test/resources/assets/base.png");

        if (!baseImage.exists()) {
            File screenshot = driver.getScreenshotAs(OutputType.FILE);
            FileUtils.copyFile(screenshot, new File("./src/test/resources/assets/base.png"));
        }

        SimilarityMatchingOptions opts =
                new SimilarityMatchingOptions().withEnabledVisualization();

        SimilarityMatchingResult res =
                driver.getImagesSimilarity(baseImage, driver.getScreenshotAs(OutputType.FILE), opts);

        if (res.getScore() >= VISUAL_THRESHOLD) {
            log.info("유사도 = [{}], 동일 화면임", res.getScore());
        } else {
            fail("유사도 = " + res.getScore() + ", 동일 화면이 아님");
        }
    }
}

실행결과:

15:23:38.305 [Test worker] INFO AppiumSampleTest -- System path = /Users/choichiwon/monimo/monimo-app-ui-automation 
15:23:39.700 [Test worker] INFO AppiumSampleTest -- 유사도 = [0.9995598793029785], 동일 화면임

 

OpenCVAppium을 이용해서 이미지 유사도를 체크할 수 있게 됐다.

하나 더 해보자. 이미지에 어떤 부분이 있는지를 체크해내는 것도 있다.

@Test
public void visualOccurrenceMatching() throws IOException {
    File baseImage = new File("./src/test/resources/assets/base.png");
    File fragment = new File("./src/test/resources/assets/fragment.png");

    OccurrenceMatchingOptions opts = new OccurrenceMatchingOptions().withEnabledVisualization();
    OccurrenceMatchingResult res = driver.findImageOccurrence(baseImage, fragment, opts);

    log.info("조각 부분의 길이 = {}", res.getVisualization().length);
    log.info("조각 부분의 x좌표 = {} | y좌표 = {}", res.getRect().getX(), res.getRect().getY());

    assertTrue(res.getVisualization().length != 0);
    assertNotNull(res.getRect());
}

 

코드를 보면 baseImage 말고 fragment가 보인다. 이 이미지는 다음과 같다.

이 이미지가 저 baseImage에 있는지도 체크할 수 있다. 있다면 x,y 좌표도 알아낼 수 있다.

실행결과:

15:30:19.701 [Test worker] INFO AppiumSampleTest -- 조각 부분의 길이 = 118624
15:30:19.707 [Test worker] INFO AppiumSampleTest -- 조각 부분의 x좌표 = 452 | y좌표 = 996

 

좌표를 알아냈으니 WebView로 된 UI라도 원하는 요소를 선택할 수 있게됐다. 

728x90
반응형
LIST

'테스트 자동화' 카테고리의 다른 글

6. Appium과 Cucumber를 이용해 UI Automation Testing  (0) 2024.04.17
5. 프로젝트 환경 설정  (2) 2024.04.17
4. Appium Inspector 연결  (0) 2024.04.17
3. APK 설치  (0) 2024.04.17
2. Appium  (0) 2024.04.17
728x90
반응형
SMALL

가장 먼저 작업할 내용은 AndroidDriver를 초기화해야 한다.

AndroidDriver는 어떤 역할을 하냐면, 연결된 Real Device를 PC에서 컨트롤할 수 있게 해주는 말 그대로 드라이버이다.

 

그리고 이 Driver는 모든 테스트를 돌리면서 딱 한개만 있으면 된다. 그래서, 싱글톤 패턴으로 드라이버를 초기화하는 클래스가 필요하다.

프로젝트 구조

└── src
    ├── main
    │   ├── java
    │   │   └── kro
    │   │       └── kr
    │   │           └── tbell
    │   └── resources
    └── test
        ├── java
        │   ├── AppiumSampleTest.java
        │   └── kro
        │       └── kr
        │           └── tbell
        │               ├── AppiumConnector.java
        │               ├── Constants.java
        │               ├── cucumber
        │               │   └── CucumberRunner.java
        │               └── stepdefinitions
        │                   └── PocFeatureStepDefs.java
        └── resources
            ├── env.yaml
            └── features
                └── poc.feature

 

자바 프로젝트를 만들면, src폴더 내부에 main, test 두 개의 폴더가 기본으로 생성된다.

여기서 test 폴더에서 우리가 원하는 구조를 만들어 나갈 것.

AppiumConnector 클래스

AppiumConnector

package kro.kr.tbell;

import io.appium.java_client.AppiumBy;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.options.UiAutomator2Options;
import lombok.extern.slf4j.Slf4j;
import org.openqa.selenium.WebElement;
import org.yaml.snakeyaml.Yaml;

import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.Map;

@Slf4j
public class AppiumConnector {
    private static AndroidDriver androidDriver;

    private static final Yaml yaml = new Yaml();

    private AppiumConnector() {}

    private static AndroidDriver getAndroidDriver() {
        if (androidDriver == null) {
            Map<String, Object> env = initializeYaml();

            String udid = (String) env.get("udid");
            String url = (String) env.get("url");

            try {
                URL appiumServer = URI.create(url).toURL();
                UiAutomator2Options options = new UiAutomator2Options().setUdid(udid);

                androidDriver = new AndroidDriver(appiumServer, options);
            } catch (MalformedURLException e) {
                log.error("[getAndroidDriver]: MalformedURLException", e);
                throw new RuntimeException(e);
            }
        }
        return androidDriver;
    }

    private static Map<String, Object> initializeYaml() {
        InputStream inputStream = AppiumConnector.class
                .getClassLoader()
                .getResourceAsStream("env.yaml");

        return yaml.load(inputStream);
    }

    public static WebElement getElementById(String id) {
        return getAndroidDriver().findElement(AppiumBy.id(id));
    }
}

 

이 클래스에서 핵심은 AndroidDriver 타입의 드라이버를 한번만 초기화하고, 해당 드라이버를 통해 Real Device에 접근하는 것이다.

외부에서 직접 접근하지 못하도록 private으로 선언한 두 개의 필드.

private static AndroidDriver androidDriver;
private static final Yaml yaml = new Yaml();

 

AndroidDriver 타입의 androidDriver 필드는 Real Device와 통신하기 위한 드라이버이다.

Yaml 타입의 yaml 필드는 resources 폴더 내 .yaml 파일에서 설정한 변수들을 가져오기 위해 필요한 필드이다. 

 

그래서 드라이버를 초기화하거나, 이전에 초기화했다면 기존 드라이버를 반환하는 메서드 getAndroidDriver().

private static AndroidDriver getAndroidDriver() {
    if (androidDriver == null) {
        Map<String, Object> env = initializeYaml();

        String udid = (String) env.get("udid");
        String url = (String) env.get("url");

        try {
            URL appiumServer = URI.create(url).toURL();
            UiAutomator2Options options = new UiAutomator2Options().setUdid(udid);

            androidDriver = new AndroidDriver(appiumServer, options);
        } catch (MalformedURLException e) {
            log.error("[getAndroidDriver]: MalformedURLException", e);
            throw new RuntimeException(e);
        }
    }
    return androidDriver;
}

일단은 이 메서드는 private으로 만들었다. 외부에서 사용하지 않아도 될 것 같아서.

외부에서 사용안해도 되는 이유는 이후에 차차 만들면서 이해할 수 있다.

 

가장 첫번째는, 이 클래스의 클래스 변수인 androidDriver가 초기화 되지 않았는지를 판단한다. 초기화 되지 않았다면 초기화해야 한다.

initializeYaml() 메서드는 .yaml 파일을 읽어오기 위해 필요한 작업을 하는 메서드이다.

private static Map<String, Object> initializeYaml() {
    InputStream inputStream = AppiumConnector.class
            .getClassLoader()
            .getResourceAsStream("env.yaml");

    return yaml.load(inputStream);
}

 

내용은 간단하다. env.yaml 파일을 읽어서 InputStream으로 넣고 yaml이 해당 스트림을 읽으면 끝.

# env.yaml
url: http://0.0.0.0:4723
udid: HVA1FG23

 

yaml 변수에 저장된 값 중 udid, url 값을 읽어온다. 읽어오면 두가지 작업을 한다.

1. URL 타입으로 변환

2. UiAutomator2Options로 Capabilities를 설정할 수 있는데 여기서는 딱 하나 Udid만 설정했다.

 

AndroidDriver 인스턴스를 만들어낸 후 반환한다. 

 

이제 다음 메서드를 보자.

public static WebElement getElementById(String id) {
    return getAndroidDriver().findElement(AppiumBy.id(id));
}

이 메서드는 드라이버를 통해 Real Device와 연결해서 특정 Resource ID를 통해 UI 요소를 가져오는 메서드이다.

앞으로 이 메서드처럼 WebElement 자체를 반환하는 메서드만 public으로 만들어서 외부에서 사용하면 될 것 같아 드라이버를 받는 getAndroidDriver() 메서드는 private으로 선언했다.

 

코드 리팩토링

지금 상태에서 코드의 개선이 많이 필요해 보인다. 코드를 개선해보자.

 

첫번째, getAndroidDriver() 메서드는 멀티쓰레드 환경에서 안전하지 않다.

동시에 여러 쓰레드가 접근할 때 인스턴스가 여러번 생성될 수 있다. 이 부분을 해결해보자.

 

◾️ synchronized 키워드와 Double-Check Locking.

private static AndroidDriver getAndroidDriver() {
    if (androidDriver == null) {
        synchronized (AppiumConnector.class) {
            if (androidDriver == null) {
               // androidDriver 인스턴스 초기화 
            }
        }
    }
}

synchronized 키워드는 메서드나 코드 블럭에 대한 동시 접근을 제한해서 한 시점에 하나의 스레드만이 그 영역을 실행할 수 있게 하는 역할을 한다. 이를 사용해서 여러 스레드가 공유하는 데이터의 동시성 문제를 해결할 수 있다.

 

근데 If (androidDriver == null) 두번 체크한다.

1. 성능 최적화: 첫번째 if로 체크해서 null이 아니라면 synchronized 블록을 비롯한 추가적인 처리 없이 바로 반환된다. 이는 대부분의 호출에서 락을 획득하는 비용을 줄일 수 있다.

2. 스레드 안정성 보장: 만약, 첫번째 검사에서 null임이 판명되면, synchronized 블록으로 진입한다. 이 블록 내에서 한번 더 검사하는 이유는 두번째 스레드 이상이 동시에 블록 안으로 들어와 대기하고 있는 경우, 첫번째 스레드가 이미 객체를 생성했을 가능성을 다시 한번 검사하기 위함이다. 즉, 이중 검사를 통해 객체가 중복 생성되는 것을 방지한다.

 

◾️ try-with-resourceInputStream 자원 해제 및 에러 처리

private static Map<String, Object> initializeYaml() {
    try (InputStream inputStream = AppiumConnector.class
            .getClassLoader()
            .getResourceAsStream("env.yaml")) {

        if (inputStream == null) {
            throw new IllegalStateException("env.yaml not found.");
        }

        return yaml.load(inputStream);
    } catch (IOException e) {
        log.error("[initializeYaml]: Error occurred when loading yaml file ", e);
        throw new RuntimeException(e);
    }
}

 

1. InputStream은 사용 후 반드시 닫아야 한다. 닫는 코드를 작성하거나 아예 try-with-resource 구문으로 사용후 끝나면 자동으로 닫아주는 방식을 택해 자원 해제를 해준다.

2. 찾고자 하는 yaml 파일이 없는 경우를 대비해 if (inputStream == null) 체크를 한다.

3. yaml 파일을 읽어들이는 중 발생하는 에러를 catch 구문으로 처리한다.

 

 

◾️공개 메서드인 경우 명확한 문서화

/**
 * 주어진 ID를 가진 웹 요소를 찾아 반환합니다.
 * 이 메서드는 안드로이드 드라이버를 사용하여 애플리케이션에서 해당 ID를 가진 요소를 검색합니다.
 *
 * @param id 찾고자 하는 웹 요소의 resource id 입니다.
 * @return WebElement 객체를 반환합니다. ID에 해당하는 요소가 없는 경우 {@code null}을 반환할 수 있습니다.
 * @throws org.openqa.selenium.NoSuchElementException 요소를 찾을 수 없는 경우 발생합니다.
 * @throws IllegalStateException AndroidDriver가 초기화되지 않았거나 접근할 수 없는 경우 발생합니다.
 * @throws org.openqa.selenium.WebDriverException 드라이버와의 통신 중 문제가 발생한 경우 발생합니다.
 * */
public static WebElement getElementById(String id) {
    return getAndroidDriver().findElement(AppiumBy.id(id));
}

외부에서 이 메서드를 가져다가 사용하는 클라이언트 코드 쪽(여러 서비스)에서 어떤 메서드인지 명확히 이해할 수 있게 JavaDoc을 활용하자. 어떤 파라미터를 줘야 하는지, null을 반환할수 있는지 없는지, 어떤 에러를 던질 수 있는지 등을 말이다.

 

리팩토링 후 최종 코드

AppiumConnector

package kro.kr.tbell;

import io.appium.java_client.AppiumBy;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.options.UiAutomator2Options;
import lombok.extern.slf4j.Slf4j;
import org.openqa.selenium.WebElement;
import org.yaml.snakeyaml.Yaml;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.Map;

@Slf4j
public class AppiumConnector {
    private static AndroidDriver androidDriver;

    private static final Yaml yaml = new Yaml();

    private AppiumConnector() {}

    private static AndroidDriver getAndroidDriver() {
        if (androidDriver == null) {
            synchronized (AppiumConnector.class) {
                if (androidDriver == null) {
                    Map<String, Object> env = initializeYaml();

                    String udid = (String) env.get("udid");
                    String url = (String) env.get("url");

                    try {
                        URL appiumServer = URI.create(url).toURL();
                        UiAutomator2Options options = new UiAutomator2Options().setUdid(udid);

                        androidDriver = new AndroidDriver(appiumServer, options);
                    } catch (MalformedURLException e) {
                        log.error("[getAndroidDriver]: MalformedURLException", e);
                        throw new RuntimeException(e);
                    }
                }
            }
        }
        return androidDriver;
    }

    private static Map<String, Object> initializeYaml() {
        try (InputStream inputStream = AppiumConnector.class
                .getClassLoader()
                .getResourceAsStream("env.yaml")) {

            if (inputStream == null) {
                throw new IllegalStateException("env.yaml not found.");
            }

            return yaml.load(inputStream);
        } catch (IOException e) {
            log.error("[initializeYaml]: Error occurred when loading yaml file ", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 주어진 ID를 가진 웹 요소를 찾아 반환합니다.
     * 이 메서드는 안드로이드 드라이버를 사용하여 애플리케이션에서 해당 ID를 가진 요소를 검색합니다.
     *
     * @param id 찾고자 하는 웹 요소의 resource id 입니다.
     * @return WebElement 객체를 반환합니다. ID에 해당하는 요소가 없는 경우 {@code null}을 반환할 수 있습니다.
     * @throws org.openqa.selenium.NoSuchElementException 요소를 찾을 수 없는 경우 발생합니다.
     * @throws IllegalStateException AndroidDriver가 초기화되지 않았거나 접근할 수 없는 경우 발생합니다.
     * @throws org.openqa.selenium.WebDriverException 드라이버와의 통신 중 문제가 발생한 경우 발생합니다.
     * */
    public static WebElement getElementById(String id) {
        return getAndroidDriver().findElement(AppiumBy.id(id));
    }
}

 

Runner 클래스 만들기

BDD 방법론에 맞게 Gherkin 문법으로 테스트 시나리오를 작성을 하고, 그 시나리오를 수행하려면 Cucumber의 도움을 받아야 한다.

Cucumber가 Gherkin 테스트 시나리오를 수행할 수 있게 해주는 도구이다.

 

CucumberRunner

package kro.kr.tbell.cucumber;

import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;
import org.junit.runner.RunWith;

@RunWith(Cucumber.class)
@CucumberOptions(
        features = {"src/test/kro/kr/tbell/features"},
        glue = "stepDefs"
)
public class CucumberRunner {

}

 

@RunWith(Cucumber.class) 애노테이션은 JUnit 프레임워크에서 테스트를 실행할 때 사용되는 실행자(runner)를 지정한다.

여기서 Cucumber.class를 사용함으로써, JUnit은 표준 테스트 실행자 대신 Cucumber 테스트 실행자를 사용하게 됩니다.

 

이렇게 하면 다음과 같은 작업을 한다.

  • Feature 파일 파싱: features 옵션에서 지정된 경로 내의 .feature 파일들을 찾아서 파싱한다. 이 파일들은 Gherkin 언어로 작성된 사용자 스토리나 비즈니스 요구사항을 담은 테스트 시나리오들이라고 보면 된다.
  • 스텝 정의와 연결: 실행자는 각 스텝(Given, When, Then)이 구현된 자바 메서드와 .feature 파일 내의 스텝을 연결한다. 이 연결은 glue 옵션에서 지정된 패키지 내에서 이루어진다.
  • 테스트 실행: 연결된 스텝 정의를 사용하여 실제 테스트를 실행하고 결과를 보고한다.

결과적으로 @RunWtih(Cucumber.class)는 Cucumber를 사용하여 BDD 접근 방식으로 정의된 테스트를 JUnit 환경에서 실행할 수 있도록 설정한다. 


Feature 파일 만들기

poc.feature

Feature: POC Feature

  Scenario: 테스트 케이스 1번
    When 개발 HTTPS 서버 버튼 클릭
    Then 간편 비밀번호 입력 문구가 노출된다

 

BDD 접근 방식으로 정의된 Gherkin 문법으로 만들어진 테스트 시나리오를 가진 poc.feature 파일이다.

.feature 파일은 대분류 Feature가 있고 소분류 Scenario가 있다.

 

Feature는 이 .feature 파일이 어떤 부분을 커버하는지를 어떤 화면도 좋다. 어떤 파트도 좋다. 큰 분류를 담당한다.

Scenario는 그 파트에서 커버되어야 할 시나리오들을 쭉 작성하는데 하나하나의 시나리오를 말한다.

 

그래서 Given - When - Then 키워드로 시나리오를 작성한다.

StepDefinition 파일 만들기

저 Feature 파일에 정의한 각각의 스텝들(Given, When, Then)에 대한 코드를 작성하는 부분이다.

PocFeatureStepDefs

package kro.kr.tbell.stepDefs;

import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import kro.kr.tbell.AppiumConnector;
import kro.kr.tbell.Constants;
import lombok.extern.slf4j.Slf4j;
import org.openqa.selenium.WebElement;

@Slf4j
public class PocFeatureStepDefs {

    @When("개발 HTTPS 서버 버튼 클릭")
    public void clickDevHttpsServerBtn() {
        WebElement devHttpsServerBtn =
                AppiumConnector.getElementById(Constants.DEV_HTTPS_SERVER_BUTTON_ID);

        devHttpsServerBtn.click();
    }


    @Then("간편 비밀번호 입력 문구가 노출된다")
    public void assertSimplePasswordText() {
        AppiumConnector
                .getElementById(Constants.SIMPLE_PASSWORD_TEXT_ID)
                .isDisplayed();
    }
}

 

이렇게 @When, @Then 애노테이션으로 어떤 스텝인지 명확하게 알 수 있어 가시성이 뛰어나다.

그리고 그 안에서 위에서 만든 싱글톤 패턴의 AppiumConnector 클래스의 클래스 변수 AndroidDriver를 가져와서 원하는 요소를 찾아내고 요소에 대해 어떤 행위를 한다. @Then에서 isDisplayed()는 보이지 않으면 그 자체로 에러를 반환하기 때문에 따로 Assertion이 필요가 없다. 

 

파라미터로 넘겨주는 값은 상수값으로 따로 정의를 했다.

Constants

package kro.kr.tbell;

public interface Constants {
    String DEV_HTTPS_SERVER_BUTTON_ID = "id-button-1"; //개발 HTTPS 서버 버튼 Resource ID
    String SIMPLE_PASSWORD_TEXT_ID = "id-title-1"; // 간편 비밀번호를 입력해주세요 문구 Resource ID
}

저 Resource ID는 Appium Inspector를 통해 알 수 있다. [아래 사진 참고]

테스트 실행

이제 제일 중요한 테스트를 직접 실행해보는 시간이다. 테스트 실행은 매우 간단하게 IDE에서 할 수 있다.

.feature 파일을 보면 좌측에 테스트 실행 버튼이 있고 Scenario 옆에 있는 버튼은 해당 시나리오만, Feature 옆에 있는 버튼은 해당 .feature 파일의 모든 시나리오를 수행한다.

 

실행해보면 JUnit으로 테스트하듯 똑같이 테스트가 진행된다. 

 

결과

 

테스트 실행을 IDE로 해봤지만 Jekins Pipeline을 사용하려면 IDE로 실행하는 법만 알아선 안된다.

 

Gradle + Cucumber 테스트 실행 (CLI)

우선, 커맨드라인으로 테스트를 실행하기 앞서, Gradle을 사용할땐 자주 사용되는 실행명령어가 있으면 그것을 task로 만들어 낼 수가 있다. 그래서 이 cucumber 실행 테스트를 task로 만들어보자.

 

build.gradle

configurations {
    cucumberRuntime {
        extendsFrom testImplementation
    }
}

tasks.register('cucumberRun') {
    dependsOn assemble, testClasses
    doLast {
        javaexec {

            main = 'io.cucumber.core.cli.Main'
            classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output
            args = ['--glue', 'kro.kr.tbell.stepdefinitions',
                    'classpath:features',
                    '--plugin', 'pretty',
                    '--plugin', 'html:build/cucumber-report.html'
            ]
        }
    }
}

이 코드는 cucumber test를 위해 build.gradle에 추가적으로 설정해야 하는 코드이다.

 

configurations {
    cucumberRuntime {
        extendsFrom testImplementation
    }
}

Gradle Configuration은 빌드 과정에서 사용되는 종속성들을 관리하는 일종의 컨테이너 역할을 한다.

 

cucumberRuntime 이라는 configuration을 만들고, extendsFrom testImplementationcucumberRuntimetestImplementation으로 선언된 모든 종속성을 상속받는다는 의미이다. 즉,  testImplementation으로 선언된 모든 라이브러리 및 파일들이 cucumberRuntime에도 속하게 된다.

 

이렇게 설정하면, Cucumber와 관련된 테스트 실행 시 필요한 모든 종속성을 cucumberRuntime에 포함시킬 수 있으며, 추가적인 종속성을 cucumberRuntime에만 지정하여 관리할 수도 있다.

 

tasks.register('cucumberRun') {
    dependsOn assemble, testClasses
    doLast {
        javaexec {

            main = 'io.cucumber.core.cli.Main'
            classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output
            args = ['--glue', 'kro.kr.tbell.stepdefinitions',
                    'classpath:features',
                    '--plugin', 'pretty',
                    '--plugin', 'html:build/cucumber-report.html'
            ]
        }
    }
}

cucumberRun 이라는 이름의 task를 만든다.

dependsOnGradle 빌드 스크립트에서 사용되는 키워드로, 하나의 Task가 다른 하나 또는 여러 Task의 실행 결과에 의존한다는 의미이다. 즉, 지정된 Task가 실행되기 전에 의존하는 Task들이 먼저 완료되어야 한다는 것을 의미한다.

 

그럼 저기서는 assemble, testClasses 두 개의 Task들이 먼저 완료가 되어야 한다는 것을 말한다.

  • assemble: 이 Task는 일반적으로 프로젝트의 모든 메인 소스 세트를 컴파일하고, 필요한 모든 리소스를 처리하며, 실행 가능한 Artifact(예: JAR파일)를 빌드하는 데 사용된다. assemble task는 메인 소스 코드가 변경되었을 때 변경사항을 반영하여 새로운 아티팩트를 만들어낸다.
  • testClasses: 이 Task는 프로젝트의 테스트 소스 코드를 컴파일한다. 테스트를 실행하기 전에 테스트 소스 코드가 최신 상태인지 확인하고 필요한 경우 컴파일을 수행하여 테스트 실행 준비를 마친다. testClasses Task는 테스트 코드에 대한 변경사항이 있을 때마다 테스트를 다시 컴파일하여 최신 상태로 유지한다.

 

그 다음 doLast는 Gradle 태스크의 생명주기 중 '실행'단계가 끝난 후 실행할 작업을 추가하는 메서드다. 태스크가 주 작업을 완료한 후 실행되어야 하는 추가적인 작업들을 정의할 때 사용된다. 그래서 do'Last'이다. 보통은 그래서 정리 작업, 로깅, 조건부 추가 작업등을 위해 사용된다. 

 

그 안에서 javaexec가 있는데 이건 Gradle에서 Java 프로그램을 실행하기 위한 built-in 함수다. 이 메서드를 통해 Java 애플리케이션을 실행하거나, Java 기반의 커맨드라인 도구를 호출할 수 있다. javaexec 블록 내에서는 실행할 Java 클래스, 클래스패스, 프로그램 인자 등을 설정할 수 있다.

 

  • main: javaexec에서 실행할 메인 클래스를 지정한다. 여기서는 Cucumber의 커맨드라인 인터페이스인 'io.cucumber.core.cli.Main'이 지정됐다. 이 클래스는 Cucumber 테스트를 실행하는 엔트리 포인트이다.
  • classpath: 실행 시 클래스패스를 지정한다. configuration.cucumberRuntime + sourceSets.main.output + sourceSets.test.output을 통해 Cucumber 실행에 필요한 모든 종속성과 컴파일된 클래스 파일들이 포함된 클래스패스를 구성했다. 'sourceSets.main.output' 이 녀석은 프로젝트의 메인 소스 셋 (src/main/java및 src/main/resources에 위치한 소스들)이 컴파일된 후 생성된 모든 클래스 및 리소스 파일들의 출력 위치를 가리킨다. 'sourceSets.test.output' 이 녀석은 테스트 소스 셋(src/test/javasrc/test/resources)이 컴파일 된 후 생성된 모든 클래스 및 리소스 파일들의 출력 위치를 나타낸다.
  • args: 프로그램 실행 시 건네 줄 인자를 작성한다.
    • --glue: Cucumber가 스텝 정의를 찾을 수 있는 패키지 경로
    • classpath:features: Cucumber가 feature 파일들을 찾을 경로 classpath에 src/test/resources가 정의되어 있으니 그 안에 features 폴더에서 .feature 파일을 찾겠다는 의미가 된다.
    • --plugin pretty: Cucumber 실행 결과를 사람이 읽기 좋게 출력하는 플러그인을 활성화
    • --plugin html:build/cucumber-report.html: 테스트 결과를 HTML 형식으로 build/cucumber-report.html에 저장

 

이렇게 설정한 Task를 커맨드라인을 통해 실행만 하면 된다.

./gradlew cucumberRun // gradlew 또는 gradle로 실행하면 된다.

728x90
반응형
LIST

'테스트 자동화' 카테고리의 다른 글

Appium, OpenCV를 활용한 Visual Testing  (0) 2024.04.25
5. 프로젝트 환경 설정  (2) 2024.04.17
4. Appium Inspector 연결  (0) 2024.04.17
3. APK 설치  (0) 2024.04.17
2. Appium  (0) 2024.04.17
728x90
반응형
SMALL

IntelliJ 프로젝트 만들기

이제 IntelliJ로 프로젝트 환경 설정을 해보자. 우선, Gradle 프로젝트를 하나 만들자.

이름과 경로는 적절하게 설정을 해준다.

Build system은 Gradle로 설정하자. 난 Gradle을 좋아하니까.

 

JDK버전은 11이상이면 좋은데 난 가장 최신 버전인 21을 사용하겠다.

그 외 나머지는 기본 설정으로하고 'Create' 클릭

 

build.gradle

가장 먼저 확인할 파일은 역시 build.gradle 파일이다. 

plugins {
    id 'java'
}

group = 'kro.kr.tbell'
version = '1.0.0'

repositories {
    mavenCentral()
}

dependencies {
    testImplementation platform('org.junit:junit-bom:5.10.0')
    testImplementation 'org.junit.jupiter:junit-jupiter'
}

test {
    useJUnitPlatform()
}

 

가장 간단한 상태이다. 여기서 appium 관련 라이브러리를 추가해야한다.

Appium java client 추가하기

dependencies {
    ...
    testImplementation 'io.appium:java-client:9.2.2'
    implementation 'io.appium:java-client:9.2.2'
    ...
}

저렇게 두 개를 해놔야 테스트 파일이 아닌곳에서도 Appium을 사용할 수 있고 내가 원하는 구조 또한 그렇다. 그래서 testImplementation, implementation 모두 추가해주자.

참고로 버전은 9.2.2가 가장 최신버전이다. (2024년 4월 17일 기준)

 

 

추가하고 빌드를 하면 이렇게 External Libraries에 매우 많은 것들이 추가된다.

io.appium.java-client를 내려받기 위해 필요한 sub-dependencies가 이렇게나 많다.

Cucumber 라이브러리 추가하기

Cucumber는 BDD 개발 방법론에 맞게 작성된 Gherkin 테스트 시나리오를 실제로 실행할 수 있도록 해주는 툴이라고 했다.

그래서 이 툴 역시 내려받아야 한다.

build.gradle

dependencies {
   ...
   implementation 'io.cucumber:cucumber-java:7.4.1'
   implementation 'io.cucumber:cucumber-junit:7.4.1'
   ...
}

 

그 외 유용한 라이브러리 추가하기

Lombok, Slf4j, SnakeYAML을 설치한다.

// Lombok
compileOnly 'org.projectlombok:lombok:1.18.30'
annotationProcessor 'org.projectlombok:lombok:1.18.30'
testCompileOnly 'org.projectlombok:lombok:1.18.30'
testAnnotationProcessor 'org.projectlombok:lombok:1.18.30'

//SLF4J API 모듈
implementation 'org.slf4j:slf4j-api:2.0.9'
testImplementation 'org.slf4j:slf4j-api:2.0.9'

// Logback Classic 구현 (SLF4J의 구현체)
implementation 'ch.qos.logback:logback-classic:1.4.14'
testImplementation 'ch.qos.logback:logback-classic:1.4.14'

// SnakeYAML
implementation 'org.yaml:snakeyaml:2.2'
testImplementation 'org.yaml:snakeyaml:2.2'

 

SnakeYAML은 .yaml 파일에 내가 정의한 key/value를 넣었을 때, 원하는 key에 해당하는 value를 읽어들이는 방법이다.

이렇게까지 라이브러리를 다운받으면 지금 당장 필요한 모든 라이브러리는 다 받았다. 이제 작업을 해보자.

728x90
반응형
LIST

'테스트 자동화' 카테고리의 다른 글

Appium, OpenCV를 활용한 Visual Testing  (0) 2024.04.25
6. Appium과 Cucumber를 이용해 UI Automation Testing  (0) 2024.04.17
4. Appium Inspector 연결  (0) 2024.04.17
3. APK 설치  (0) 2024.04.17
2. Appium  (0) 2024.04.17
728x90
반응형
SMALL

이제 Appium Inspector를 사용해서 UI 테스트를 하고자하는 App과 연결해보자.

 

UiAutomator2 Driver 설치

실행 하기전 "UiAutomator2" Driver를 설치해야 한다.

appium driver install uiautomator2

성공적으로 설치가 되면 다음과 같은 화면이 나오면 된다.

Appim Server 실행

이제 appium을 실행한다.

appium

이러한 화면이 나오면 성공!

Appium Inspector 실행

이제 Appium Inspector를 실행한다.

 

  • Remote Host: 0.0.0.0
  • Remote Port: 4723
  • Remote Path: /
  • Capability Builder
    • appium:udid: 연결할 Device udid
    • platformName: Android
    • appium:automationName: UiAutomator2
    • appium:skipUnlock: false
    • appium:autoGrantPermissions: true

여기서 Capability Builder 옵션은 그때그때 다를 수 있다. 나는 이렇게 해도 무방하기에 이렇게 설정했다.

이 옵션들이 어떤 내용인지 더 자세히 알고 싶다면 다음 링크를 참조하자.

 

GitHub - appium/appium-uiautomator2-driver: Appium driver for Android UIAutomator2

Appium driver for Android UIAutomator2. Contribute to appium/appium-uiautomator2-driver development by creating an account on GitHub.

github.com

 

우측 하단 Start Session 버튼을 클릭하면 연결된다.

이렇게 내가 연결한 Device의 화면을 캡쳐해서 각 요소들에 대한 정보들을 뽑아낼 수 있다.

 

728x90
반응형
LIST

'테스트 자동화' 카테고리의 다른 글

6. Appium과 Cucumber를 이용해 UI Automation Testing  (0) 2024.04.17
5. 프로젝트 환경 설정  (2) 2024.04.17
3. APK 설치  (0) 2024.04.17
2. Appium  (0) 2024.04.17
1. BDD, Gherkin, Cucumber  (0) 2024.04.17
728x90
반응형
SMALL

App 테스트를 하려면 App을 설치할 Device가 필요하다.

이 때 두가지 경우로 나눌 수 있다.

  • Emulator: Android Virtual Device (AVD)
  • Real Device

Emulator를 사용하는 경우에는 Android Studio를 사용해서 AVD를 설치하면 된다. 이거는 이 포스팅 범주에서 벗어난 범위이기 때문에 여기에 작성하지는 않겠다.

 

Real Device를 사용하는 경우에는 USB Debugging을 활성화 시키면 된다. 나의 경우 Real Device를 사용할 것.

PC에 Real Device를 연결하고, USB Debugging 활성화가 됐다고 가정하고 시작한다.

 

Real Device에 APK 설치

우선, 내 APK 경로는 다음 경로에 있다.

/Users/choichiwon/apk/apkfile.apk

 

Real Device에 APK를 설치하는 방법은 다양한데 CLI를 통해 설치할 것.

CLI를 통해 설치하려면 Android SDK가 설치되어 있어야 한다. (물론 설치를 떠나서 Appium과 Andorid App을 사용해서 테스트 하려면 무조건 필요하다).

Android SDK 설치 방법
가장 쉬운 방법은 Android Studio를 설치하고, 이 경로에서 원하는 버전으로 설치하면 된다.
Settings -> Appearance & Behavior -> System Settings -> Android SDK

설치할 때 이 두가지를 설치하면 된다. 
- Android SDK Platform
- Android SDK Platform-Tools

 

설치가 다 끝나면 Platform-tools가 위치한 경로가 있다. 나의 경로는 다음과 같다.

/Users/choichiwon/Library/Android/sdk/platform-tools


여기에 "adb"라는 도구가 있다. 이건 Android Debug Bridge의 약자로 이 도구는 개발자가 Android 기기와 상호작용할 수 있도록 하는 명령줄 도구인데 이 도구를 이용해서 현재 연결된 디바이스 리스트를 가져올 수 있다.

./adb devices

 

연결된 디바이스 하나가 보인다. 이렇게 디바이스가 나오면 "adb"를 통해서 이 디바이스와 통신을 할 수 있다.

"adb" 명령어를 이용해서 apk를 설치해보자.

./adb install -r /Users/choichiwon/apk/apkfile.apk

이 명령어가 "adb"를 이용해서 apk를 설치하는 명령어이다. 경로는 당연히 apk가 있는 경로를 지정해주면 되고, 디바이스가 하나만 연결되어 있으면 디바이스 정보를 주지 않아도 된다. "./adb devices" 를 입력했을 때 여러 디바이스가 나오면 어떤 디바이스에 설치할지 명시해줘야 한다. 아래가 그 예시이다.

./adb -s HVA1FG23 install -r "apk file path"

 

옵션 정보는 다음과 같다.

  • -r: replace. 이미 설치되어 있으면 지우고 재설치, 없으면 그냥 설치

입력하면 이러한 문구가 나온다.

Success가 나오면 정상 설치가 된 것.

 

728x90
반응형
LIST

'테스트 자동화' 카테고리의 다른 글

6. Appium과 Cucumber를 이용해 UI Automation Testing  (0) 2024.04.17
5. 프로젝트 환경 설정  (2) 2024.04.17
4. Appium Inspector 연결  (0) 2024.04.17
2. Appium  (0) 2024.04.17
1. BDD, Gherkin, Cucumber  (0) 2024.04.17
728x90
반응형
SMALL

멀티 플랫폼(Web, Mobile, Desktop, ...)을 대상으로 UI 자동화 테스트를 할 수 있게 해주는 오픈 소스 프로젝트인 Appium.

이 Appium을 사용해서 Mobile App 자동화 테스트를 진행해 볼 생각이다.

 

Appium Install

우선, Appium을 사용하려면 설치를 해야한다.

 

Install Appium - Appium Documentation

Install Appium You can install Appium globally using npm: Note Other package managers are not currently supported. After installation, you should be able to run Appium from the command line: You should see some output that starts with a line like this: [Ap

appium.io

이 링크에서 Appium을 설치할 수 있다. 간단하게는 NPM이 설치되어 있다면 다음 명령어를 실행하자.

npm i -g appium

 

설치가 다 되면 다음 명령어를 실행해서 정상적으로 설치됐는지 확인해보자.

appium -v

Upgrade Appium version

Appium이 설치되어 있는 상태인데, 버전이 Outdate 상태라면 다음 명령어로 업데이트해주자.

npm update -g appium

 

Appium Inspector

Appium을 사용하면 반드시 같이 사용할 수 밖에 없는 GUI Inspector tool.

좋은점은 모든 종류의 Mobile App을 지원한다는 것이고 무료이다.

 

요래 생긴 툴이다.

 

Install

 

Releases · appium/appium-inspector

A GUI inspector for mobile apps and more, powered by a (separately installed) Appium server - appium/appium-inspector

github.com

이 링크에서 OS별, 버전별로 설치가 가능하다. 가장 최신의 버전을 선택해서 내려받으면 된다.

나의 경우 MacOS이기 때문에 .dmg 파일을 내려받아서 설치했다.

 

그리고 설치하고 실행하면 이 경고 문구가 나온다.

이제 다음 명령어를 수행하면 된다. 

xattr -cr "/Applications/Appium Inspector.app"

 

요 명령어를 수행하면 정상적으로 실행이 될 것이다. 아니면 System Settings > Privacy & Security 탭에 가서 하단에 Security 쪽에 보면 "Appium Inspector가 검사되지 않은 앱인데 실행을 할거냐?" 뭐 이렇게 물어보는데 [Open Anyway] 버튼 클릭하면 된다.

 

실행한 화면은 다음과 비슷하면 된다.

 

728x90
반응형
LIST

'테스트 자동화' 카테고리의 다른 글

6. Appium과 Cucumber를 이용해 UI Automation Testing  (0) 2024.04.17
5. 프로젝트 환경 설정  (2) 2024.04.17
4. Appium Inspector 연결  (0) 2024.04.17
3. APK 설치  (0) 2024.04.17
1. BDD, Gherkin, Cucumber  (0) 2024.04.17
728x90
반응형
SMALL

테스트 자동화 인프라 구축 프로젝트를 여러번 진행해 오면서 알게된 내용과 필요한 내용을 정리하고자 한다.

 

BDD(Behavior Driven Development)

행위 주도 개발이라는 뜻의 개발방법론인 BDD

소프트웨어 개발 과정을 개선하기 위해 사용되는 방법론이다.

Agile 개발 방법론의 일종으로, 소프트웨어 프로젝트의 개발을 가이드하기 위해 행동 기반의 언어를 사용한다.

행동 기반의 언어라는 건 이런 것이다.

1. B 화면이 보인다.
2. B화면의 우측 상단에 [A] 버튼을 클릭한다.
3. 클릭한 버튼 하단 셀렉트 박스에 [...] 텍스트가 확인된다.

 

이 방법론의 핵심은 개발자, 테스터, 비즈니스 분석가 등 프로젝트에 참여하는 모든 사람이 소프트웨어의 동작을 명확하고 이해하기 쉬운 방식으로 정의하고, 이러한 동작을 기반으로 커뮤니케이션하며, 결국에는 테스트와 개발을 진행하는 것이다.

 

BDD는 기능적 요구사항을 사람이 읽을 수 있는 언어로 작성된 시나리오로 변환하여 기술적인 사양과 비즈니스 요구 사이의 이해를 돕는다.

 

Gherkin

BDD의 구현을 위해 사용되는 도메인 특화 언어(Domain Specific Language, DSL)이다. 

사람이 읽을 수 있는 말로 작성되며, 특정 기능이 어떻게 동작해야 하는지를 시나리오 형식으로 기술한다.

Gherkin으로 작성된 시나리오는 주로 Given - When - Then 형식을 따른다.

  • Given: 테스트의 전제 조건
  • When: 수행할 액션
  • Then: 예상 결과

이런 방식으로 Gherkin은 비즈니스 로직을 명확하게 기술하고 이를 바탕으로 테스트 케이스를 만들어 내는데 이상적인 문법이다.

 

Cucumber

Gherkin으로 작성된 시나리오도 결국 테스트 시나리오이니까 실행할 수 있는 도구가 필요하다.

그 도구가 Cucumber라는 소프트웨어 툴이다.

 

이는, BDD 프로세스를 지원하기 위해 설계되었으며, Gherkin 시나리오를 자동화 된 테스트로 변환한다.

Cucumber는 다양한 프로그래밍 언어를 지원하고 개발자가 Gherkin 시나리오를 바탕으로 테스트 코드를 작성할 수 있도록 해준다.

결과적으로 Cucumber를 사용함으로써 팀은 비즈니스 요구사항이 정확히 이해되고 충족되는지를 보다 쉽게 검증할 수 있다.

 

BDD, Gherkin, Cucumber와 자동화

이 방법론과 도구는 함께 작동할 때 가장 효과적이다. BDD는 프로세스와 커뮤니케이션의 틀을 제공하며, Gherkin은 이 틀 내에서 비즈니스 요구사항을 명확하고 일관된 형식으로 기술하는 방법을 언어적으로 풀어 제공한다. 마지막으로 Cucumber는 Gherkin으로 작성된 시나리오를 실행 가능한 테스트로 전환하여, 요구사항이 제대로 충족되었는지를 자동으로 검증할 수 있게 해준다.

 

이러한 조합은 비즈니스 요구사항을 정확하게 이해하고, 이를 기반으로 고품질의 소프트웨어를 더 빠르게 개발하는 데 도움을 준다.

또한 사람이 읽을 수 있는 언어로 애플리케이션의 기능을 설명하듯 테스트 시나리오를 만들기 때문에  애플리케이션의 특정 기능에 대한 문서화가 대체될 수 있다는 점에서 장점을 가진다고 볼 수 있다.

그래서 결국 더 예측 가능하고 관리 가능하며 비즈니스 요구사항과 기술적 구현 사이의 간극을 줄이는 데 초점을 맞추고 있다.

 

728x90
반응형
LIST

'테스트 자동화' 카테고리의 다른 글

6. Appium과 Cucumber를 이용해 UI Automation Testing  (0) 2024.04.17
5. 프로젝트 환경 설정  (2) 2024.04.17
4. Appium Inspector 연결  (0) 2024.04.17
3. APK 설치  (0) 2024.04.17
2. Appium  (0) 2024.04.17

+ Recent posts