클래스팅에 합류하여 Test Automation Engineer 포지션으로 마지막 Sync-up(수습) 1달을 앞두고 있다. 1월 24일 설 연휴 전에 입사했고, 연휴와 온보딩 그리고 짧은 2월로 인해, 1달 같은 2달을 보낸 기분이다. 워킹데이가 적은 것이 Sync-up을 진행하는 나로선 오히려 부담으로 다가왔지만, 이런 것조차 핑계로 만들면 앞으로 아무것도 해낼 수 없고 도전할 수 없다.
나는 클래스팅에서 Test Automation을 위한 전체 Workflow를 구축하고 관리 및 유지보수와 개선을 담당하게 될 예정이다. 입사 후 4주 동안 테스트 자동화 프레임워크를 리서치했고 클래스팅만의 테스트 자동화 구축 기준을 통해, 각각의 프레임워크를 직접 활용해보며 비교 분석했다. 이후 테스트 자동화에 필요한 프레임워크를 셋업하고 동일한 테스트 케이스에 대해서 web, Android, iOS E2E 테스트 코드를 작성하여 Workflow에 필요한 각 서비스 간의 Integration을 진행하고 있다. 최종적으로 내가 목표로 한 workflow는 아래이다.
Integration
- E2E 프레임워크
- 클라우드 리얼 디바이스 서비스
- 테스트 매니지먼트 서비스
- CI/CD
- bitrise
재밌는 사실은, CTO님께서 해당 workflow를 원하고 계셨고, 마침 내가 개인적으로 세운 목표와 일치하게 되었는데, 이런 게 바로 덕업 일치가 아닐까 싶다. 가끔 주변에 계신 개발자스러운 QA Engineer 분들께서 우리 CTO님을 뵙고 싶다는 얘길 건네 올 때면, 기쁘기도 하고 Classting에서의 커리어가 나에게 앞으로 많은 도움이 되지 않을까 라는 생각이 든다.
더불어 Testing Korea Tech Conf를 준비하며 알게 된 나와 비슷한 성향과 비슷한 길을 걸어가고자 하는 QA Engineer 분들이 적지만은 않아서, 주최 예정인 Testing Korea Tech Conf도 보다 수월하게 진행되지 않을까 싶다.
본론으로 들어가, 입사 첫 주에 직면했던 문제에 대해 정리하여 해결 방법을 공유하고자 한다. 아마도 E2E 프레임워크에서 구글 Gmail 서비스를 통해 회원가입 같은 플로우에서 인증번호를 받아와야 하는 로직이 필요하다면, 이 글이 조금이나마 도움이 될 것 같다. 내가 사용한 방법은 Web, Android, iOS 모두 가능한 방법이다. 지금 생각해보면 별 것 아닌 것 같지만, 처음 해보는 나로선 당시 막막했기에, 누군가도 내 심정과 똑같을 거라 생각한다. 나와 비슷한 막막함을 느끼는 사람이 1명이라도 있다면, 작은 공유를 통해 작지만 큰 도움이 되길 바란다.
사용 프레임워크
Web-WebdriverIO
Mobile-WebdirverIO, Appium
직면했던 문제
element not interactable Exception
매번 달라지는 인증번호 값을 어떻게 가져오는지?
구글 Page Element와 Interaction 중인 상태에서, 어떻게 다시 클래스팅 회원가입 Page의 Element와 Interaction 할 것인가?
해결 과정 및 백그라운드
문제 1. element not interactable Exception
Background
당시 상황은 이랬다. 웹에서 회원가입 테스트 진행 중, 구글 메일에 수신된 인증번호를, 클래스팅 인증번호 입력 필드에 가져오는 로직이 필요했다. WebdriverIO는 멀티 탭 지원이 가능하기에 쉽게 구현될 거라 생각했다. 그런데, 새로운 크롬 Tab인 구글 로그인 화면에서 아이디 입력 후 비밀번호 입력을 위해 ‘다음’ 버튼 클릭 시, element not interactable Exception이 발생하여 Test Fail 처리되었다.
해결 과정
‘다음’ 버튼 클릭하는 로직 필요 이유가, 비밀번호 입력을 위한 Test Condition이자, Test Step에 해당되니, 어떻게 해서든 ‘다음’ 버튼을 클릭한 상태로 만들면 된다고 생각했다. 구글 페이지 자체를 테스트하는 것이 아니기 때문에, 구글 페이지에 접근하려는 행위의 목적 또한, 인증번호를 가져오기 위함이므로, 목적 달성에 적합한 액션을 진행했다. 따라서 요소를 click() 시에 오류가 발생했으니, click() 하지 않고, browser에서 Enter 동작을 하는 browser.keys(”\uE007”)로 위 문제는 해결되었다.
그런데, browser.keys(”\uE007”) 이후, 패스워드 입력 필드가 나타나면, Session Delete 되어 테스트 중단 현상이 발생했다. https://stackoverflow.com/questions/55637344/getting-element-not-interactable-for-below-code 를 참고하여 힌트를 얻었고, Enter 동작 후, 패스워드 입력란이 나타나기 전에 browser.pause(ms)로 해결했다.
참고로 Cypress는 멀티 탭을 지원하지 않고, 구글과 같은 서비스에 접근하려는 시도를 권장하지 않아서 WebdriverIO를 웹 자동화 프레임워크로 결정하게 되었다.
배운 점과 부족했던 점
아직까진 사용 중인 프레임워크의 아키텍처에 대한 높은 이해를 지니고 구현하고 있는 것은 아니다. 하지만 위 경험을 통해 경험적으로 각 페이지 또는 스크린 전환 간에 특정한 로직 사이에는 browser.pause(ms) 값을 적절히 줘야 한다는 것을 직관적으로 알 수 있게 되었고, 해당 오류를 통해 이후 테스트 코드 작성에 위와 같은 관점에서 큰 도움이 되었다.
문제 2. 매번 달라지는 인증번호 값을 어떻게 가져올 건지?
Background
당시 상황은 이랬다. element not interactable Exception 문제를 해결하고, Gmail에 접근 후, 클래스팅 인증번호 관련 메일 접근까지 완료된 상태였다. 그런데, 인증번호는 사용자가 회원 가입할 때마다 매번 다른 번호가 수신된다. 매번 달라지는 인증 번호 값을 클래스팅 회원가입 Page에 넣는 로직이 필요했다.
해결 과정
특정 변수에 인증번호를 넣고, 그 인증번호 값을 가진 변수를, 회원가입 인증번호 입력하는 곳에 addvalue()로 넘겨주는 방향을 생각했고, 관련 작업을 진행했다. 우선 인증번호 값이 어떤 데이터인지 확인이 필요했다. WebdriverIO에서 제공하는 getValue()를 사용하여 console.log()로 확인 결과 null로 나타났다. getValue()가 아닌 getText()로 동일한 과정을 진행했고, 인증번호가 String 값 형태로 나타났다. 이 과정에서 웹 같은 경우는 인증번호 : 524423 형식으로 나타났는데, 전체 Locator 중, 인증번호 숫자 영역이 항상 동일한 스타일이 DOM 요소에 나타나는 것을 발견했고, 인증번호에 해당하는 로케이터를 직접 생성하여(span [style$='color:#00 c896'] 해당 Locator에 getText로 가져온 String 타입의 값을, VerficationNumber 변수에 저장하여, setValue() 또는 addValue()에 값을 넘겨줘서 해결했다.
웹은 위와 같은 형태로 해결이 되었지만, 모바일 같은 경우 Appium Inspector를 세팅하고, 인증번호 영역을 확인해보면, 실제로 필요한 건 인증번호에 해당하는 숫자 영역인데, 웹과 달리 인증번호 : 524423 형태로 Locator를 생성할 수밖에 없어서, 숫자만 가져오는 방법이 필요했다. 전체적인 흐름은 웹과 동일하나, 가져온 인증번호 관련 Element를 slice()로 필요한 부분과 필요치 않는 부분을 분리하여, 인증번호만 가져오는 방법을 통해 해결했다.
배운 점과 부족했던 점
테스트 자동화도 결국 코드를 작성하는 행위이기 때문에, SW Engineer 만큼의 프로그래밍 언어를 다룰 수 있는 역량을 갖춰야겠다는 목표를 다시 한번 리마인드 시켜준 문제였다. 개발자분들이 보기엔 굉장히 쉽고 간단한 로직일 수 있겠지만, 6년간 비개발자로 IT에 근무해오면서, 문제 해결에 있어서 스스로에게 부족한 부분이 많다는 것을 느꼈고, 앞으로 개인 학습을 통해 배워야 할 것들이 많다는 것을 일깨워준 문제였다.
문제 3. 구글 Page의 Element와 Interaction 중인 상태에서, 어떻게 다시 클래스팅 회원가입 Page의 Element와 Interaction 할 것인가?
Background
당시 상황은 이랬다. element not interactable Exception 문제도 해결했고, 매번 달라지는 인증번호 값을 가져오는 방법도 해결했다. 하지만 크롬 탭 전환을 통해 구글 Page의 Element와 Interaction 중인 상태였고, 해당 상태에서 클래스팅 회원가입 Page에 인증번호를 넣는 시도를 하게 되면, 관련 오류가 나타나게 되었다. 클래스팅 페이지와 다시 Interaction 가능한 로직이 필요했다.
해결 과정
1. 현재 Open 상태의 크롬 탭 개수는 2개, 만약 각각의 크롬 탭도 내부적으로 특정한 값을 가지고 있다면, 그 값을 찾아서 반환 후, browser 프로토콜로 실행하면, 회원가입 페이지와 Interaction 가능한 상태가 되지 않을까? 생각했다. getWindowHandle()로 콘솔 출력 결과 String 타입의 CDwindow-BEFCD35 F05 F537 FF6 BB75 BC8 E95 DC5 A5 값이 나타났다. 해당 값이 구글 페이지 값인지, wdio.conf에 정의된 base url 값인지 확인 결과, 현재 Interaction 중인 페이지인 구글 페이지 값으로 확인되었다. 보다 정확한 확인을 위해 getUrl()로 추가 테스트 결과, 현재 Interaction 구글 Gmail URL이 나타났다. 따라서 getWindowHandle()은, 최상위 브라우저 값을 String 형태로 반환시키기 때문에, 문제 해결에 적절하지 않았다.
2. 모든 Session을 종료하지 않고, 구글 화면의 Session만 Close 시키면, 최상위 브라우저 값이 구글에서 클래스팅 페이지로 자동 전환되지 않을까?라는 생각에서 출발했다. 하지만 테스트 결과 모든 Session이 종료되면서 테스트가 중단되었다. 해당 방법도 문제 해결에 적절하지 않았다.
3. 열려 있는 모든 페이지를 리스트 형태로 반환하여 해당 리스트의 인덱스 번호로 접근하는 건 어떨까?라는 생각에서 출발했다. 이때 알게 된 사실인데, getWindowHandle()와 getWindowHandles()의 차이점을 알게 되었다. getWindowHandles()로 열려 있는 모든 페이지 확인 결과, 리스트 형태로 반환되었다. 추가 테스트 결과, 첫 번째 인덱스가 base url에 해당하는 클래스팅 페이지였고, 이후 인덱스는 테스트 로직을 실행시킨 순서에 의해 결정되는 것으로 파악했다. 공식 문서에 Handle 반환 순서가 임의적인 것으로 확인되어, 만약 의도치 않는 인덱스 값을 가져와서 Test Fail이 발생한다면, 해당 인덱스가 구글 페이지인지 클래스팅 페이지인지 검사하는 로직이 추가되어야 하지만, 우선 그 문제는, 문제가 발생하면 해결하기로 했다. 결과적으로 getWindowHandles() 값에서 첫 번째 인덱스를 switchToWindow() 인자로 넣으면 클래스팅 페이지로 전환 및 구글 페이지와의 Interaction이 중단되고, 클래스팅 페이지와의 Interaction이 가능해진다. 그렇다고 해서 구글 페이지가 닫히는 건 아니고, 만약 구글 페이지로 다시 돌아가야 한다면, getWindowHandles()로 인덱스를 찾아서, switchToWindow()로 전환하면 된다.
배운 점과 부족했던 점
스택오버플로우 또는 구글에 검색하기 전에, 공식문서를 한번 더 꼼꼼히 읽어야겠다는 생각이 들었다. 해당 문제는 첫 번째 해결 과정인 getWindowHandle() 테스트 과정에서, 조금만 더 꼼꼼히 살펴보고 getWindowHandles()와의 차이를 알았다면, 문제 해결에 많은 시간을 단축할 수 있었기 때문이다. 이런 부분이 스스로 많이 부족하다 느끼고 있고, 스스로도 인지하고 있다. 앞으로 품질에 조금 더 집중하는 SW Engineer 역량을 쌓아가면서 차차 보완할 생각이다.
끝으로
이외에도 정말 다양한 문제들을 마주했고 해결하며 Test Automation Workflow 구축을 진행하는 중이다.
RequestError: socket hang up, Error: Timeout of 60000ms exceeded, 그레이 박스 E2E 프레임워크인 Detox 셋업 간에 RN 개발 환경 미숙으로 인해 발생한 개발 사이드에서의 여러 문제들, lipo -info Classting.app/Classting로 앱 파일 아키텍처 확인 결과, arm64로 되어 있어서 시뮬레이터 구동 간에 Appium 서버 사이드 에러인 500 error를 마주한 경험들, 로컬 환경에서는 웹, 모바일 모두 구축 가능성을 확인했으나, 외부 서비스 연동 후 클라우드 인프라에서 리얼디바이스 실행 간에 An element could not be located on the page using the given search parameters로 인해 에뮬레이터와 리얼디바이스가 서로 동일한 기기여도 UI 계층 구조가 다르다는 것을 배웠고, 프레임워크의 config 파일 분리 작업과 외부 서비스 관련 config 파일의 capabilities 설정 문제를 해결한 과정들. 지금도 여전히 처음 보는 에러를 수도 없이 만나고 있고, 해결한 에러와 그렇지 못한 에러가 수두룩한 상태이다.
가끔은 하루가 어떻게 흘러갔는지도 모를 정도로 명확히 해결한 에러는 없는데 오늘 하루가 끝나버리는 상황도 경험해보며 많은 허탈과 좌절을 느꼈지만, 끝내 발생한 문제가 해결되었을 때의 짜릿함은, 처음 하프마라톤을 완주했을 때의 그 감정과 굉장히 비슷했다.
돌이켜보면 에러를 만난 건 큰 행운이 아니었을까 생각된다. 에러를 마주할 때 가장 많이 성장하고, 에러를 마주할 때 내가 그동안 알고 있는 것들에 대한 팩트가 조금이나마 검증되기 때문이다.
여전히 많이 부족하고 앞으로 몇 년간 많이 부족할 것 같다. 하지만 지금 나에게 주어진 역할이 너무 재밌고 흥미롭다. 나의 커리어 미래는 솔직히 잘 모르겠다. 하지만 확실한 건 1년 2년이 지났을 때, 소프트웨어 품질에 조금 더 집중하는 SW Engineer가 되어있지 않을까 생각해본다.
고민은 능력이 생겼을 때 다시 해보고, 고민 자체를 즐기지 않으려 한다. 우선은 능력을 쌓고, 그러한 과정을 통해 선택지의 폭을 넓히고 나서, 앞으로의 커리어 목표와 방향을 결정해도 늦지 않고, 그러한 순서가 되어야 오늘 하루를 의미 없는 생각과 행위에 쏟지 않을 수 있다.
아무쪼록 입사 첫 주 직면했던 문제의 해결 과정에 대한 포스팅은 여기까지다. 입사 후 지금까지 대부분의 커뮤니케이션을 SW Engineer 분들과 진행하며 지내왔다. 내가 처한 문제에 대해 함께 공감해주고 도와주려 하는 모습들이 인상 깊게 다가왔으면서도, 한편으론 SW Engineer분들의 본업에 나로 인해 버퍼가 생기지 않았으면 하는 마음에, 이 포스팅을 발행하는 오늘도 개인 학습을 멈출 수 없다.
내게 주어진 문제는 내 선에서 해결하고 싶은 사람이 되고 싶고, 그러한 SW Engineer(품질에 조금 더 집중하는)가 되고 싶다. 또한 내가 관심 있는 분야는, 그것이 일(work)로 느껴지지 않고, 하나의 취미 생활로 느껴지기 때문에, 이러한 라이프 스타일을 즐길 수 있게 되었지만, 때로는 마음의 조급함을 덜어내고 조금 더 천천히 여유 있게 커리어를 쌓아가는 자세도 필요한 것 같다.
추후 전체 플로우가 구축되고 유지보수에 접어들 무렵, SW Engineer 신입 역량에 준하는 스킬 셋과 이해도를 보유한 채, 테스트 자동화 기술에 대한 깊은 지식이 담긴 포스팅을 꾸준히 이어나가고 싶다. 그리고 그러한 포스팅과 내가 추진 중인 Testing Korea Tech Conf를 통한 외부 발표들이, 현재 컨택 중인 SW Engineer 스러운 QA Engineer 분들과의 꾸준한 소통을 통해 국내 QA 직군에 긍정적인 변화의 조금이나마 줄 수 있길 바라본다.
Happy Testing!
Testing Korea Tech Conf 메인 운영진 추가 모집 중이오니, 관심 있으신 분들께서는 링크드인 DM 부탁드립니다.
- 웹, 모바일 테스트 자동화 경험자
- 프로그래밍 언어 1개 이상 사용 경험자
- 프로그래밍 언어 1개에 대해 해당 언어가 지닌 특성까지 깊게 배우고자 노력하시는 분
- 업무 외 시간에 늘 배움을 갈구하는 태도
- SW 개발에 관심이 많으신 QA Engineer
- SW QA 직군이 지금과는 다른 방향으로 발전해야 한다고 생각하시는 분