본문으로 건너뛰기

WebDriver Manager와 MEIPASS

· 약 21분
Dongmin Yu

코드 예제

if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'):
     chrome_path = os.path.join(sys._MEIPASS, 'chromedriver.exe')
 else:
      chrome_path = ChromeDriverManager().install()
      chrome_service = Service(chrome_path)

코드 설명

이 코드는 PyInstaller로 생성된 번들 실행 파일에서 스크립트가 실행되는지 여부를 확인합니다. 만약 그렇다면 번들에 포함된 Chrome 드라이버의 경로를 설정합니다. 그렇지 않으면 ChromeDriverManager를 사용하여 Chrome 드라이버를 설치하고 경로를 설정합니다. 그런 다음 지정된 경로를 사용하여 Chrome 서비스 객체를 생성합니다. webdriver.Chrome()을 사용하여 드라이버 인스턴스를 만들 때 이 코드가 없으면 에러가 발생할 수 있습니다. 이는 Chrome 드라이버의 경로가 올바르게 설정되지 않았기 때문일 수 있습니다. 이 코드는 Chrome 드라이버의 경로를 올바르게 설정하여 이러한 문제를 해결하는 데 도움이 됩니다. getattr(sys, 'frozen', False)sys 모듈의 frozen 속성을 가져오는 코드입니다. frozen 속성은 스크립트가 PyInstaller로 생성된 번들 실행 파일에서 실행되는 경우에만 존재합니다. 따라서 이 코드는 스크립트가 번들 실행 파일에서 실행되는지 여부를 확인하는 데 사용됩니다. Falsefrozen 속성이 존재하지 않을 경우 반환되는 기본값입니다. hasattr(sys, '_MEIPASS')sys 모듈에 _MEIPASS 속성이 있는지 여부를 확인하는 코드입니다. _MEIPASS 속성은 PyInstaller로 생성된 번들 실행 파일에서 스크립트가 실행되는 경우에만 존재하며, 번들의 임시 디렉토리 경로를 나타냅니다. 따라서 이 코드도 스크립트가 번들 실행 파일에서 실행되는지 여부를 확인하는 데 사용됩니다. _MEIPASS는 PyInstaller의 내부 변수로, 약어가 아닙니다. 이 변수는 PyInstaller로 생성된 번들 실행 파일에서 스크립트가 실행되는 경우에만 존재하며, 번들의 임시 디렉토리 경로를 나타냅니다. 이 경로는 PyInstaller가 번들을 실행할 때 생성하는 임시 디렉토리로, 번들에 포함된 모든 리소스 파일이 이 디렉토리에 추출됩니다. 셀레니움(Selenium) 웹 드라이버의 생성자에 파라미터가 없는 경우, 셀레니움은 시스템 환경 변수 PATH에서 Chrome 드라이버의 실행 파일을 찾습니다. 따라서 Chrome 드라이버의 실행 파일이 PATH에 포함된 디렉토리에 있어야 합니다. 서비스 객체를 생성자의 service 파라미터로 전달하는 경우, 셀레니움은 서비스 객체가 가리키는 경로에서 Chrome 드라이버의 실행 파일을 찾습니다. 이 경우 PATH에 포함된 디렉토리에 Chrome 드라이버의 실행 파일이 없어도 됩니다. 서비스 객체를 사용하면 Chrome 드라이버의 경로를 명시적으로 지정할 수 있습니다. executable_path 변수를 사용하여 Chrome 드라이버의 경로를 지정하는 것과 서비스 객체를 사용하여 Chrome 드라이버의 경로를 지정하는 것은 기능적으로 동일합니다. 둘 다 Chrome 드라이버의 경로를 명시적으로 지정할 수 있습니다. 그러나 서비스 객체를 사용하면 추가적인 옵션을 설정할 수 있습니다. 예를 들어, 서비스 객체의 service_args 속성을 사용하여 Chrome 드라이버의 로그 파일 경로나 로그 수준을 지정할 수 있습니다. 따라서 서비스 객체를 사용하는 것이 더 유연하고 확장 가능한 방법입니다. service_args 속성은 서비스 객체를 생성할 때 전달할 수 있는 선택적 파라미터입니다. 이 속성은 Chrome 드라이버의 명령줄 인수를 지정하는 데 사용됩니다. 예를 들어, Chrome 드라이버의 로그 파일 경로와 로그 수준을 지정하려면 다음과 같이 service_args 속성을 사용할 수 있습니다.

from selenium.webdriver.chrome.service import Service
chrome_service = Service(executable_path='/path/to/chromedriver',
                         service_args=['--log-path=/path/to/logfile', '--verbose'])

위 코드에서 --log-path 인수는 Chrome 드라이버의 로그 파일 경로를 지정하고, --verbose 인수는 로그 수준을 지정합니다. 이 외에도 다양한 명령줄 인수를 사용하여 Chrome 드라이버의 동작을 제어할 수 있습니다. Chrome 드라이버의 실행 파일에 --help 커맨드를 사용하면 사용 가능한 모든 명령줄 인수의 목록을 확인할 수 있습니다. 예를 들어, Windows에서는 명령 프롬프트에서 다음과 같이 입력할 수 있습니다.

chromedriver.exe --help

Linux나 macOS에서는 터미널에서 다음과 같이 입력할 수 있습니다.

./chromedriver --help

위 명령을 실행하면 Chrome 드라이버가 지원하는 모든 명령줄 인수와 각 인수의 설명이 출력됩니다. 이 정보를 참조하여 service_args 속성에 전달할 인수를 선택할 수 있습니다. WebDriverManager는 Chrome 드라이버의 설치와 관리를 자동화하는 라이브러리입니다. 이 라이브러리는 시스템에 설치된 Chrome 브라우저의 버전과 호환되는 Chrome 드라이버의 버전을 자동으로 감지하고 설치합니다. WebDriverManager를 사용하면 Chrome 드라이버의 버전을 수동으로 관리할 필요가 없습니다. WebDriverManager가 자동으로 Chrome 브라우저의 버전과 일치하는 Chrome 드라이버의 버전을 설치하므로, Chrome 브라우저와 Chrome 드라이버의 버전이 항상 일치합니다. 만약 시스템에 이미 설치된 Chrome 드라이버의 버전이 Chrome 브라우저의 버전과 일치하지 않는 경우, WebDriverManager는 새로운 버전의 Chrome 드라이버를 다운로드하여 설치합니다. 따라서 WebDriverManager를 사용하면 Chrome 드라이버의 버전 관리가 편리해집니다.

셀레니움 (Selenium)

셀레니움(Selenium)은 여러 가지 프로그래밍 언어를 지원합니다. 이 중에서 가장 인기 있는 언어는 Java입니다. 그러나 Python, C#, Ruby, JavaScript 등도 셀레니움을 사용하는 데 널리 사용되는 언어들입니다. 각 언어의 성능은 다양한 요인에 따라 달라질 수 있습니다. 따라서 특정 언어가 다른 언어보다 더 좋은 성능을 보장하는 것은 아닙니다. 셀레니움을 사용할 때 중요한 것은 개발팀이 사용하는 언어와의 호환성, 개발자 커뮤니티의 지원 정도, 개발자의 경험 등이 있습니다.

References

(1) What are the different programming languages supported by Selenium? - QAFox (2) Selecting a Programming Language for Selenium Automation Testing (3) Best Language for Selenium Testing - Tutorsbot (4) Selecting a Programming Language to build Selenium Test Automation Suite - Cigniti (5) Puppeteer vs Selenium: Core Differences | BrowserStack (6) Puppeteer vs Selenium - A Comparison Between 2 Automated Testing Libraries (7) Cheerio vs Puppeteer for Web Scraping in 2023: In-Depth Guide

Puppeteer와 Selenium은 모두 웹 자동화 도구로, 웹 페이지의 테스트와 크롤링 등에 사용됩니다. 하지만 이들은 몇 가지 차이점이 있습니다. Puppeteer는 Google에서 개발한 Node.js 라이브러리로, Chrome과 Chromium 브라우저를 제어하는 데 사용됩니다. Puppeteer는 Chrome DevTools Protocol을 사용하여 브라우저를 제어하며, 기본적으로 headless 모드로 실행됩니다. 이는 화면에 브라우저 창을 띄우지 않고 백그라운드에서 실행되는 것을 의미합니다.

Puppeteer

Puppeteer는 스크린샷 캡처, PDF 생성, 웹 스크래핑 등 다양한 기능을 제공합니다. 하지만 Puppeteer는 Chrome과 Chromium 브라우저만 지원하며, JavaScript 언어만 사용할 수 있습니다. 반면 Selenium은 여러 가지 브라우저와 프로그래밍 언어를 지원하는 웹 자동화 도구입니다. Selenium은 Java, Python, C#, Ruby 등 다양한 언어로 테스트 스크립트를 작성할 수 있으며, Chrome, Firefox, Safari 등 다양한 브라우저에서 테스트를 실행할 수 있습니다. Selenium은 큰 커뮤니티 지원과 다양한 라이브러리 및 확장 기능을 제공합니다. Puppeteer와 Selenium의 선택은 사용자의 요구사항과 환경에 따라 달라질 수 있습니다. Puppeteer는 Chrome 브라우저에서 빠른 성능을 제공하며, JavaScript 언어로 간단하게 사용할 수 있습니다. 반면 Selenium은 여러 가지 브라우저와 언어를 지원하며, 큰 커뮤니티 지원을 받습니다.

Selenium

Selenium은 웹 애플리케이션의 자동화 테스트를 위한 도구로, 모바일 애플리케이션의 테스트에는 직접 사용할 수 없습니다. 하지만 Appium이라는 모바일 애플리케이션 자동화 테스트 도구가 있습니다. Appium은 Selenium과 유사한 API를 제공하며, iOS와 Android 모바일 애플리케이션의 자동화 테스트를 지원합니다.

Appium

Appium은 Selenium WebDriver와 호환되며, Selenium과 유사한 방식으로 사용할 수 있습니다. 따라서 Selenium을 사용해본 경험이 있다면 Appium을 쉽게 사용할 수 있습니다. Appium은 Java, Python, Ruby, JavaScript 등 다양한 언어로 테스트 스크립트를 작성할 수 있습니다. Appium은 Dan Cuellar에 의해 2011년에 개발되었습니다. 처음에는 "iOSAuto"라는 이름으로 C# 프로그래밍 언어로 작성되었습니다. 2012년 8월에 Apache 2 라이선스로 오픈소스화되었고, 2013년 1월에 Sauce Labs가 Appium의 개발을 지원하면서 Node.js로 코드가 재작성되었습니다. Appium은 모바일 애플리케이션의 자동화 테스트를 위한 오픈소스 도구로, iOS와 Android 모바일 애플리케이션의 테스트를 지원합니다. Appium은 Selenium과 유사한 API를 제공하며, Java, Python, Ruby 등 다양한 언어로 테스트 스크립트를 작성할 수 있습니다.

References

(1) Appium - Wikipedia (2) Appium: Mobile App Automation Made Awesome. (3) What is Appium and How does it work? An Overview and Its Use Cases

크로스플랫폼 프레임워크로 만들어진 모바일 앱의 자동화 테스트

크로스플랫폼 프레임워크로 만들어진 모바일 앱의 경우에도, 안드로이드와 iOS 각각의 테스트 스크립트를 작성해야 합니다. 이는 안드로이드와 iOS가 각각 다른 운영 체제이기 때문입니다. 따라서 Appium을 사용하여 안드로이드와 iOS 앱을 테스트할 때에는 각각의 테스트 스크립트를 작성해야 합니다. 하지만 Appium은 Selenium과 유사한 API를 제공하므로, Selenium을 사용해본 경험이 있다면 Appium을 쉽게 사용할 수 있습니다. 또한 Appium은 Page Object Model과 같은 디자인 패턴을 사용하여 테스트 코드의 재사용성을 높일 수 있습니다. 이를 통해 안드로이드와 iOS 앱의 테스트 코드를 최대한 비슷하게 작성할 수 있습니다. Appium을 사용하려면 먼저 Appium 서버를 설치해야 합니다. Appium 서버는 Node.js로 작성되었으므로 Node.js와 npm이 설치되어 있어야 합니다. Appium 서버를 설치하려면 다음 명령을 실행합니다.

npm install -g appium

Appium 서버를 설치한 후에는 Appium 클라이언트 라이브러리를 사용하여 테스트 스크립트를 작성할 수 있습니다. Appium 클라이언트 라이브러리는 Java, Python, Ruby 등 다양한 언어로 제공됩니다. 예를 들어, Python으로 테스트 스크립트를 작성하려면 다음 명령으로 Appium의 Python 클라이언트 라이브러리를 설치할 수 있습니다.

pip install Appium-Python-Client

Appium 클라이언트 라이브러리를 설치한 후에는 Selenium과 유사한 방식으로 테스트 스크립트를 작성할 수 있습니다. 예를 들어, 다음은 Python으로 작성된 간단한 Appium 테스트 스크립트입니다.

from appium import webdriver
desired_caps = {
    "platformName": "Android",
    "platformVersion": "9.0",
    "deviceName": "Android Emulator",
    "app": "/path/to/your/app.apk"
}
driver = webdriver.Remote("http://localhost:4723/wd/hub", desired_caps)
# 여기에 테스트 코드를 작성합니다.
driver.quit()

위 코드에서 desired_caps 변수는 테스트할 모바일 장치와 앱의 정보를 지정합니다. webdriver.Remote 메서드를 사용하여 Appium 서버에 연결하고, 이후에는 Selenium과 유사한 방식으로 테스트 코드를 작성할 수 있습니다. Appium 서버는 Node.js로 작성되었기 때문에 Appium 서버를 실행하려면 Node.js가 설치되어 있어야 합니다. 하지만 Appium 클라이언트 라이브러리는 여러 가지 언어로 제공되므로, 테스트 스크립트를 작성할 때에는 Node.js가 아닌 다른 언어를 사용할 수 있습니다. 리액트 네이티브 애플리케이션의 UI 테스트를 위해서는 Detox라는 도구를 사용할 수 있습니다. Detox는 리액트 네이티브 애플리케이션의 엔드-투-엔드 테스트를 위한 도구로, iOS와 Android 모바일 애플리케이션의 테스트를 지원합니다. Detox는 빠른 테스트 실행 속도와 안정적인 테스트 결과를 제공합니다. Detox 테스트가 실패하는 경우에는 여러 가지 원인이 있을 수 있습니다. 먼저 Detox의 문서와 트러블슈팅 가이드를 참조하여 일반적인 문제와 해결 방법을 확인해보세요. 또한 Detox 테스트의 로그를 확인하여 실패 원인을 파악할 수 있습니다. E2E 테스트의 규모를 줄이는 것 외에도, 다음과 같은 방법으로 테스트의 안정성을 높일 수 있습니다.

  • 테스트 전에 앱의 상태를 초기화하여 테스트가 항상 동일한 상태에서 시작하도록 합니다.
  • 네트워크 지연이나 애니메이션 등으로 인한 지연을 고려하여 적절한 대기 시간을 설정합니다.
  • 테스트 중 발생하는 예외 상황을 처리하는 코드를 작성합니다. 또한 Detox는 Jest와 같은 테스트 프레임워크와 함께 사용할 수 있습니다. Jest의 기능을 활용하여 테스트 코드의 구조화와 재사용성을 높일 수 있습니다. Jest는 JavaScript 테스트 프레임워크로, Detox와 함께 사용하여 리액트 네이티브 애플리케이션의 E2E 테스트를 작성할 수 있습니다. Jest를 사용하면 테스트 코드의 구조화와 재사용성을 높일 수 있습니다. 다음은 Jest와 Detox를 함께 사용하는 간단한 예제 코드입니다.
describe("Login", () => {
  beforeAll(async () => {
    await device.reloadReactNative();
  });
  it("should show login screen", async () => {
    await expect(element(by.id("loginScreen"))).toBeVisible();
  });
  it("should login successfully", async () => {
    await element(by.id("emailInput")).typeText("user@example.com");
    await element(by.id("passwordInput")).typeText("password");
    await element(by.id("loginButton")).tap();
    await expect(element(by.id("homeScreen"))).toBeVisible();
  });
});

위 코드에서 describe 함수는 Jest에서 제공하는 함수로, 관련된 테스트 케이스들을 그룹화하는 데 사용됩니다. beforeAll 함수는 테스트 그룹의 모든 테스트 케이스가 실행되기 전에 한 번만 실행되는 함수로, 이 예제에서는 앱의 상태를 초기화하는 데 사용됩니다. it 함수는 각각의 테스트 케이스를 정의하는 데 사용됩니다. 위 예제에서처럼 Jest의 기능을 활용하면 Detox 테스트 코드를 구조화하고 재사용성을 높일 수 있습니다.