이번 포스트에서는 appium에서 제공해주는 기능을 이용해 이미지 유사성을 판단해보는 기능을 기재해봅니다.
본 포스트를 확인하시기전에 이전 내용도 한 번 읽어보시면 좋습니다.
Appium 2.0부터는 server에 여러가지 plugin을 추가로 설치해서 추가적인 기능을 수행할 수 있습니다.
https://appium.io/docs/en/latest/ecosystem/plugins/#official-plugins
공식적으로 제공해주는 플러그인은 2024/03/10
현재 4개가 있고, 그 와중에 image
를 이용하는 플러그인이 있습니다.
이 플러그인은
기능을 제공하는데요, 오늘 포스트에서는 첫 번째, image comparison
기능을 간단하게 이용해보고자합니다.
일단 플러그인에서 제공하는 공식 문서는 아래 페이지인데요,
https://github.com/appium/appium/blob/master/packages/images-plugin/docs/image-comparison.md
크게 세 가지 기능이 있습니다만, 이번은 이미지 유사성을 계산해보는 기능을 파이썬에서 구현하려합니다.
appium을 이용해서 테스트를 수행할 때, 각 페이지 별로 스크린샷을 찍고, 비교대상 원본과 비교하여 일정 유사도점수 이하이면 True / False 체크를 하는 그런 기능입니다.
일단 작업 전에, 아래 로직을 실행할 컴퓨터에 opencv4nodejs
설치가 필요합니다!
이미지 비교작업을 하는데에 네이티브에 설치된 opencv 라이브러리를 이용하거든요.
npm install opencv4nodejs
만약 에러가 난다면, 아래 페이지를 참고해서 설치해주세요!
https://github.com/justadudewhohacks/opencv4nodejs?tab=readme-ov-file#installing-opencv-manually
일단 스크린샷이 저장되어있거나, 저장할 경로를 미리 계산해줍니다.
current_working_path = os.getcwd()
resource_dir_path = os.path.join(
current_working_path,
"resources",
)
저는 프로젝트 루트 디렉토리 하단에 resources
폴더를 만들었습니다. 이후에 원본이 될 파일을 저장할 폴더와, 실패한 경우의 스크린샷을 저장할 폴더의 경로를 각각 지정해주었습니다.
baseline_dir = os.path.join(resource_dir_path, "baseline_screenshots")
fail_screenshot_dir = os.path.join(resource_dir_path, "fail_screenshots")
그 다음 비교 원본 파일의 경로를 가져오고, 그와 동시에 현재 appium driver가 조작중인 화면을 base64
로 캡쳐합니다.
base_img_path = baseline_dir + "/" + screen_name + ".png"
actual_screen = self.driver.get_screenshot_as_base64()
만약 base_img_path
에 해당 파일이 존재하면, 이미지 비교로직을 실행하고, 그렇지 않으면 현재 드라이버가 조작하고 있는 페이지를 비교원본으로 간주하고 스크린샷을 저장합니다.
if os.path.exists(base_img_path):
## ~~~
else:
self.driver.save_screenshot(base_img_path)
만약 지정한 경로에 파일이 존재한다면, 이미지 유사도를 계산합니다.
일단 경로에서 with open()
으로 파일을 가져오는데, 이때 base64로 인코딩한 후 이것을 문자열로 바꿔줍니다. 파이썬에서는 데이터 처리 시에 일반적으로 문자열(str
) 을 통해 처리한다고해서 그런 것 같네요.
with open(base_img_path, "rb") as img:
base_img = base64.b64encode(img.read()).decode("ascii")
다음 driver.get_images_similarity()
를 이용해 유사도 계산을 합니다.
results = self.driver.get_images_similarity(
base64_image1=base_img, base64_image2=actual_screen, visualize=True
)
여기서 함수에 visualize
파라미터를 넘겨주지않으면, 비교해서 원본 파일과 다른 점이 있을 시에 빨간색 사각형으로 diff를 표현해주는데 그 파일을 리턴해주지 않습니다.
유사도는 1 인 경우 완전 일치로 간주합니다. 따라서, 특정 유사도 이하인 경우에는 두 개의 스크린샷에서 유의미한 차이점이 있는 것으로 간주하고 적절한 처리를 해주어야합니다.
if results["score"] < 0.99:
image_diff_path = os.path.join(
fail_screenshot_dir, "FAIL_" + screen_name + ".png"
)
with open(image_diff_path, "wb") as f:
f.write(base64.b64decode(results["visualization"]))
리턴받은 딕셔너리로 부터 score
값을 꺼내 유사도 체크를 한 후에, diff 파일을 저장할 경로를 조합해서 만들어줍니다.
그 후 with open()
을 이용해서, visualization
키의 밸류값을 디코딩하여 저장해줍니다. visualization
키의 밸류값은 diff가 기록된 png
파일의 base64 인코딩값입니다.
이제 diff 이미지를 확인합니다.
네이버 홈페이지를 대상으로 스크린샷을 찍었고, 오른쪽이 비교원본 스크린샷 / 왼쪽이 비교대상 스크린샷 입니다.
전체 코드는 아래와 같습니다.
def check_visual_quality(self, screen_name: str):
current_working_path = os.getcwd()
resource_dir_path = os.path.join(
current_working_path,
"resources",
)
baseline_dir = os.path.join(resource_dir_path, "baseline_screenshots")
fail_screenshot_dir = os.path.join(resource_dir_path, "fail_screenshots")
base_img_path = baseline_dir + "/" + screen_name + ".png"
actual_screen = self.driver.get_screenshot_as_base64()
if os.path.exists(base_img_path):
with open(base_img_path, "rb") as img:
base_img = base64.b64encode(img.read()).decode("ascii")
results = self.driver.get_images_similarity(
base64_image1=base_img, base64_image2=actual_screen, visualize=True
)
if results["score"] < 0.99:
image_diff_path = os.path.join(
fail_screenshot_dir, "FAIL_" + screen_name + ".png"
)
with open(image_diff_path, "wb") as f:
f.write(base64.b64decode(results["visualization"]))
else:
self.driver.save_screenshot(base_img_path)
appium에서 제공해주는 기능을 통해 간단하게 이미지 유사도 계산을 구현해보았습니다.
특정 유사도 이하인 경우에는 False/True를 리턴한다던지 해서 테스트케이스에서 assertion
으로 사용하시면 될 것 같고, 캡쳐한 스크린샷은 리포트에 첨부해서 바로 볼 수 있게 대응해도 될 것 같습니다.
끝!
ref