How to check page image similarity using Appium Python

Dahun Yoo·2024년 3월 10일
0

Appium with python

목록 보기
13/13
post-thumbnail

이번 포스트에서는 appium에서 제공해주는 기능을 이용해 이미지 유사성을 판단해보는 기능을 기재해봅니다.

본 포스트를 확인하시기전에 이전 내용도 한 번 읽어보시면 좋습니다.

Appium image plugin

Appium 2.0부터는 server에 여러가지 plugin을 추가로 설치해서 추가적인 기능을 수행할 수 있습니다.

https://appium.io/docs/en/latest/ecosystem/plugins/#official-plugins

공식적으로 제공해주는 플러그인은 2024/03/10 현재 4개가 있고, 그 와중에 image를 이용하는 플러그인이 있습니다.

이 플러그인은

  1. image comparison
  2. finding elements by image

기능을 제공하는데요, 오늘 포스트에서는 첫 번째, image comparison 기능을 간단하게 이용해보고자합니다.

Caculate similarity between base image and current page

일단 플러그인에서 제공하는 공식 문서는 아래 페이지인데요,

https://github.com/appium/appium/blob/master/packages/images-plugin/docs/image-comparison.md

크게 세 가지 기능이 있습니다만, 이번은 이미지 유사성을 계산해보는 기능을 파이썬에서 구현하려합니다.

appium을 이용해서 테스트를 수행할 때, 각 페이지 별로 스크린샷을 찍고, 비교대상 원본과 비교하여 일정 유사도점수 이하이면 True / False 체크를 하는 그런 기능입니다.

Pre-condition

일단 작업 전에, 아래 로직을 실행할 컴퓨터에 opencv4nodejs 설치가 필요합니다!
이미지 비교작업을 하는데에 네이티브에 설치된 opencv 라이브러리를 이용하거든요.

npm install opencv4nodejs

만약 에러가 난다면, 아래 페이지를 참고해서 설치해주세요!

https://github.com/justadudewhohacks/opencv4nodejs?tab=readme-ov-file#installing-opencv-manually

setting path

일단 스크린샷이 저장되어있거나, 저장할 경로를 미리 계산해줍니다.

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")

Save page screenshot if no base capture

그 다음 비교 원본 파일의 경로를 가져오고, 그와 동시에 현재 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)

Calculate similarity

만약 지정한 경로에 파일이 존재한다면, 이미지 유사도를 계산합니다.

일단 경로에서 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 인코딩값입니다.

Check diff image

이제 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

profile
QA Engineer

0개의 댓글