...
pywinauto - 소프트웨어 테스트 자동화
일상적으로 반복적으로 수행하는 리그레이션 테스트 작업중 단순하게 클릭 및 화면 확인 정도의 테스트 케이스는 매번 사람이 직접 눌러서 확인하기에는 너무 비효율 적이다. (앉아서 클릭하는 생산직도 아니고..)
이렇게 반복적인 단순한 작업들은 프로그램을 만들어서 자동화를 하는 것이 회사 입장에서나 사람 입장에서나 서로 편하고 좋다.
pywinauto는 윈도우 OS용 프로그램의 컨트롤 요소에, 마우스 클릭이나 키보드 입력 등을 사람이 직접 하지 않고 프로그램이 대신할 수 있도록 해주는 python 라이브러리 이다. (MAC이나 Linux에서는 당연히 사용 불가능하다)
셀레니움(Selenium)이 웹브라우저 자동화 라고 한다면, pywinauto는 윈도우 소프트웨어의 셀레니움이라고 봐도 무방하다.
보통 자동화를 매크로에 빗대어 생각하곤 하지만, 매크로의 대명사 오토핫키(autohotkey)는 윈도우 좌표값을 특정해 버튼을 클릭하는 구조이기 때문에 만일 윈도우화면창이 조금만 틀어진다면 문제가 생기게 된다.
하지만 pywinauto는 윈도우 요소들을 모두 객체로 변환하여 이를 프로그래밍적 요소로 다룰수 있게 해준다. 이를 통해서 원하는 application에 원하는 keyboard혹은 mouse 액션을 컨트롤할 수 있다.
사용법도 그렇게 어렵지 않아 비전공자분들도 손쉽게 테스트 자동화가 가능하다.
pywinauto 설치
이제 본격적으로 pywinauto 사용법을 알아보겠다.
본 포스팅에서는 NodePad++ 소프트웨어를 기준으로 자동화 프로그래밍을 진행해볼 예정이다.
먼저 파이썬 프로젝트에 pywinauto 라이브러리를 설치해 준다.
> pip3 install pywinauto
테스트할 프로그램 이름 얻기
만일 테스트할 윈도우 프로그램의 정확한 명칭을 알면 넘어가도 된다.
다만 윈도우 프로그램 이름을 잘 모르겠다면, 실행중인 프로세스에서 소프트웨어 이름 정보와 프로세스 pid를 얻을 수 있다.
from pywinauto import findwindows
# 현재 윈도우 화면에 있는 프로세스 목록 리스트를 반환한다.
# 리스트의 각 요소는 element 객체로 프로세스 id, 핸들값, 이름 등의 정보를 보유한다.
procs = findwindows.find_elements()
for proc in procs:
print(f"{proc} / 프로세스 : {proc.process_id}")
테스트할 프로그램 실행 / 연결
위에서 찾은 프로세스의 정보나 프로그램 경로를 기재하면 간단히 연결된다. 테스트할 프로그램을 연결하는 방법은 크게 두가지가 있다.
- Application().start() : 프로그램 경로를 넣어서 실행
- Application().connect() : 이미 실행되고 있는 프로그램을 연결
둘중 원하는 방법으로 연결 해주면 된다.
이때 Application() 함수의 인수로 backend 값을 지정해주어야 하는데, 값은 win32 와 uia 가 있다.
이 요소값은 어떤 종류의 프로그램을 제어하려는지 알려주는 것인데, 프로그램 개발 시 사용된 GUI 프레임워크에 따라 다르다.
- Application(backend="win32") : 메모장과 같은 old한 프로그램을 실행할때
- Application(backend="uia") : 최신 기술이 사용된 프로그램 (왠만한 요즘 프로그램들이 속함)
from pywinauto import findwindows
from pywinauto import application
app = application.Application(backend='uia')
# 프로세스의 경로를 넣어 실행해준다.
app.start("C:\\Program Files\\Notepad++\\notepad++.exe")
from pywinauto import findwindows
from pywinauto import application
app = application.Application(backend='uia')
# 1. 정규식으로 프로그램 title을 검색해서 연결하는 방법
app.connect(title_re="Notepad\+\+")
# 2. 프로세스 pid로 연결하는 방법
app.connect(process=28924)
connect를 이용하면 중간에 다른 윈도우 프로그램 창을 엑세스할수도 있다.
프로그램 위젯 객체 모두 출력
노트패드를 실행하면 위의 사진과 같이 여러 메뉴들과 버튼들이 있다.
이러한 위젯들을 객체로 접근하여 클릭하거나 등 이벤트를 발생해서 테스트를 하는 것인데, 각 위젯 객체 정보들을 출력하는 것이 바로 print_control_identifiers() 함수이다. 이 함수를 사용하면 다음과 같이 현재 window에서 호출할 수 있는 개체들을 tree 형태로 보여준다.
app = application.Application(backend='uia')
app.start("C:\\Program Files\\Notepad++\\notepad++.exe")
# 컨트롤 요소 출력
dlg = app['Notepad++'] # 변수에 노트패드 윈도우 어플리케이션 객체를 할당
dlg.print_control_identifiers() # 노트패드의 컨트롤 요소를 트리로 모두 출력
위젯 control_type 종류
print_control_identifiers() 함수로 띄운 개체 트리 리스트들을 보면 각각 컨트롤 타입이 버튼(button)인것도 있고 메뉴바(menubar) 인것도 있고 메뉴 아이템(menuitem) 인것도 있는 것을 볼 수 잇다.
이처럼 윈도우 객체는 여러가지 control_type이 존재하는데, 이들은 각각에 맞는 동작을 수행한다.
예를들어 컨트롤 타입이 Button인 경우 click() 동작을 수행할 수 있고, TreeItem 타입인 경우 select() 동작을 수행할 수 있는 식이다.
- Button
- CheckBox
- RadioButton
- GroupBox
- ComboBox
- Edit
- Header
- ListBox
- ListView
- PopupMenu
- Static
- StatusBar
- TabControl
- Toolbar
- ToolTips
- TreeView
- UpDown
위젯 이벤트 자동화 하기
버튼 요소 클릭하기
위에서 리스트한 요소들 중에 버튼 요소를 찾아서 클릭해 보는 실행을 만들어보자.
먼저 누르고자 하는 버튼 객체의 제목(title)을 얻는다.
기본적으로 윈도우 프로그램에서 커서를 버튼 위에 올려다놓고 3초정도 기다리면 네모박스로 타이틀이 뜨게되는데 이를 이용해 이름이 같은 개체를 찾아 실행해주면 된다.
print_control_identifiers() 함수로 띄운 개체 트리 리스트들에서 '최대화'를 검색해보니 위와 같이 Button - '최대화' 개체를 찾았다.
목록을 보면 배열에는 뭔가 여러 버튼이름들이 적혀있고, child_window() 라는 함수도 나열되어 있는데, 이중에 하나를 골라서 실행해주면 된다. 그러면 노트패드 윈도우 창이 확대되는 것을 확인 할 수 있다.
pywinauto에서 여러 요소명으로 접근할수있게 기능을 제공해주는건데 괜히 사람 햇깔리는 면이 없지않아 있다
dlg = app['Notepad++']
# 아래 3가지 방법중에 하나를 택해 해주면 된다.
# 버튼 클릭은 click() 함수를 통해 실행가능하다
dlg['최대화Button'].click()
dlg['Button34'].click()
dlg.child_window(title="최대화", control_type="Button")
문자열 전송하기
노트패드++ 메모장에 문자열을 전송해 글씨를 쓰도록 자동화 해보자.
type_keys(): Edit 창에 문자열을 전송하는 기능으로 {ENTER}와 같은 특수 키도 입력이 가능하다.
# 메모 입력 (띄어쓰기 하려면 반드시 with_spaces=True
dlg['Pane'].type_keys('Hello World', with_spaces=True)
# pywinauto 글자를 적고 엔터하고 test 글자 적기
dlg['pane'].type_keys('pywinauto{ENTER}test')
dlg['Pane'].type_keys("Hello pywinauto!\n\t It’s a sample text^A",
with_spaces=True, # 띄어쓰기 허용
with_newlines=True, # \n 개행 허용
pause=0.1, # 0.1초마다 타이핑
with_tabs=True # \t 탭 허용
)
메뉴 컨트롤하기
이번엔 메뉴의 파일 / 편집 / 찾기 /보기 ... 를 자동화 해보자.
테스트 시나리오는 다음과 같다.
- 파일(F) 메뉴를 누른다.
- 파일 메뉴 리스트 child 창이 생긴다
- child 창에서 새 파일(N)을 누른다.
우선 저 파일 메뉴의 객체 이름이 무엇인지 컨트롤 리스트에서 확인해보자.
저 파일 메뉴의 개체 이름은 '파일(F)' 인걸 알 수 있다. 그런데 파일 메뉴를 누르면 나오는 child 창의 리스트들을 요소 객체들은 나와있지 않다.
왜냐하면 현재 윈도우 화면에 child 리스트창이 떠있지 않은 상태로 print_control_identifiers() 함수를 실행했기 때문이다.
따라서 print_control_identifiers() 함수를 실행 순서를 바로하지말고, 먼저 파일(F) 메뉴 버튼을 누르고 child 리스트창이 윈도우 화면에 떳을때 실행하면 된다.
app = application.Application(backend='uia')
app.start("C:\\Program Files\\Notepad++\\notepad++.exe")
dlg = app['Notepad++']
# 파일 메뉴를 누른다
dlg['파일(F)'].select()
# 그러면 파일 메뉴 리스트들이 뜰텐데 이 리스트 값들도 얻기위해 다시 print_control_identifiers()를 실행해준다
dlg.print_control_identifiers()
그러면 위와 같이 파일 메뉴 리스트들이 출력되게 된다.
우리는 새파일을 누르도록 테스트 시나리오를 짯으니, '새파일(N)\tCtrl+N2' 객체명을 얻어 실행해주면 된다.
dlg['파일(F)'].select()
dlg['새 파일(N)\tCtrl+N2'].select()
참고로 위의 과정을 단 하나의 메소드로 축약하여 등록이 가능하다.
app['Notepad++'].menu_select("파일(F)->새 파일(N)")
단축키 사용하기
왠만한 윈도우 프로그램은 각 메뉴에 마다 단축키를 지원해준다. 이를 이용하면 굳이 메뉴를 일일히 클릭하는 프로세스를 거치지않고 한번에 창을 띄울수 있다.
pywinauto에도 autohotkey처럼 단축키를 처리할수 있는 기능이 있다.
- '+' : shift 키 기호
- '^' : ctrl 키 기호
- '%' : Alt 키 기호
from pywinauto import findwindows, application
from pywinauto import keyboard # 단축키 기능을 이용하기 위해서는 keyboard 객체를 import 해주어야 한다.
keyboard.send_keys('^n') # ctrl + n
keyboard.send_keys('^f') # ctrl + f
keyboard.send_keys('%c') # alt + c
keyboard.send_keys('{F1}') # F1 키
대기시간 주기
자동화 실행 속도가 너무 빨라 창이 순식간에 꺼져버리거나 그러면, time.sleep(1) 을 줘서 1초 대기하거나 하는 프로세스를 줄 수 있다.
import time
from pywinauto import findwindows, application, keyboard
# ...
keyboard.send_keys('^f') # ctrl + f
dlg['닫기Button'].click()
time.sleep(0.5) # 0.5초 대기
keyboard.send_keys('%c') # alt + c
dlg['닫기Button'].click()
창에 포커스 주기
dlg['창 이름'].set_focus()
더블 클릭
dlg['창 이름'].double_click()
PyInspect - 위젯 요소를 GUI로 확인하기
우리는 지금까지 print_control_identifiers() 함수를 일일히 실행하여 위젯 겍체의 정보를 얻어 사용해왔다.
그러나 매번 소스를 실행할때마다 확인해야 되며 더군다나 요소가 많으면 출력하기 까지 시간이 꽤 걸린다.
따라서 pywinauto는 공식적으로 GUI 툴로 한방에 윈도우 요소들을 상세 내역까지 확인할 수 있는 pyInspect를 지원한다.
PyInspect 설치
위의 깃헙 링크에 들어가서 py_inspect.py 파일을 저장하고 실행해주면 된다.
pyInspect를 사용하기 위해서는 주의사항이 있는데, 우선 당연히 Windows OS에서만 지원되며, 실행하는데 필요한 파이썬 패키지인 pywinauto, PyQt5가 install되어 있어야 한다.
# 선행 설치
> pip3 install pywinauto
> pip3 install PyQt5
# 실행
> python3 py_inspect.py
귀찮은 분들을 위해 따로 .exe 로 만들어 구글 드라이브에 올려놓았다.
※ 다만 현재 화면에 프로그램을 많이 띄운 상태라면 실행이 되게 오래걸리니 유의하자.
이 글이 좋으셨다면 구독 & 좋아요
여러분의 구독과 좋아요는
저자에게 큰 힘이 됩니다.