TesterArmy로 Expo 앱에 에이전트 기반 CI 구축하기

Expo의 Keith와 TesterArmy의 Oskar가 오픈소스 E2E로 에이전트 테스트의 세 가지 약점인 속도, 비용, 판정 불안정을 어떻게 다루는지 시연한 대담을 방법론 순서로 재구성했다. 결정적 단계와 에이전트 단계의 혼합과 행동 캐시, 사용자가 정의하는 에이전트, 증거로 거르는 버그 배시, 비결정성의 한계, 엔진 분리와 EAS 클라우드 시뮬레이터, 기존 테스트 이전을 다룬다.

TesterArmy로 Expo 앱에 에이전트 기반 CI 구축하기

Expo의 Keith가 TesterArmy의 CEO Oskar와 함께 E2E를 시연한다. TesterArmy는 웹과 모바일을 함께 지원하는 에이전트 기반 테스트 플랫폼이고, E2E는 그 플랫폼의 오픈소스 코어로, 발표 직전 주에 공개되었다. 대담은 저장소와 터미널 화면을 열어 놓고 진행되며, 시청자 질문이 중간중간 들어온다.

E2E가 풀려는 문제는 에이전트 테스트의 세 가지 약점이다. 느리고, 비싸고, 판정이 불안정하다. 답은 결정적 단계와 에이전트 단계를 한 테스트 안에서 섞고, 에이전트가 한 번 수행한 행동은 캐시해 다음 실행에서 결정적으로 재생하며, 에이전트를 정의하는 방식과 사용할 도구를 사용자가 통제하는 것이다. 여기에 플랫폼별 부분을 엔진으로 분리한 구조와 EAS 클라우드 시뮬레이터가 더해지면, 같은 테스트가 웹과 iOS와 Android에서 CI에서 실행된다.

Oskar는 비결정성이 줄어들 뿐 사라지지 않는다는 점, 탐색 에이전트가 없는 버그를 보고할 수 있다는 점, OTP 같은 기능은 아직 오픈소스에 없다는 점을 분명히 말한다. 이 노트는 이러한 한계까지 함께 기록한다.

결정적 단계와 에이전트 단계를 한 테스트에서 섞고, 행동은 캐시한다

E2E의 핵심은 혼합 API다. 장기적으로 직접 유지하고 싶은 행동은 선택자로 결정적으로 작성하고, 유지하기 부담스러운 행동은 에이전트에게 넘긴다. 얼마나 많은 부분을 에이전트에게 맡길지는 사용자가 정한다. 에이전트는 행동뿐 아니라 판정에도 쓸 수 있다. 공개 후 사용자들이 시도한 사례 하나는 모든 행동을 결정적으로 실행한 뒤 마지막 판정에만 에이전트를 쓰는 것이다. 이렇게 하는 이유는 요소가 화면에 있다는 조건은 기존 assertion으로 확인할 수 있지만, 텍스트가 화면 밖으로 밀리지 않았는지와 전체적으로 보기 좋은지 같은 시각적 판단은 기존 assertion으로 작성하기 어렵기 때문이다. 이 판정은 Figma 디자인이나 기준 스크린샷과 대조하는 방식으로도 확장할 수 있다.

expo-testerarmy-agentic-ci-e2e-11-07
결정적 테스트 단계와 에이전트 행동·판정을 한 테스트에서 섞는 E2E 저장소의 API 예시

에이전트는 첫 실행에서 앱을 탐색하며 행동 경로를 만들고, 이후에는 캐시된 행동을 결정적으로 재생한다. 앱이 달라져 기존 경로를 실행할 수 없으면 다시 화면을 확인하고 작업을 이어 간다. Oskar가 든 예는 간헐적인 팝업이다. 앱에 크리스마스 할인 팝업 하나가 추가되면 기존 테스트 전체가 실패하고 개발자가 별도 처리를 넣어야 하지만, 목표를 '워크스페이스를 Pro 요금제로 업그레이드하기'로 지정해 두면 에이전트가 팝업을 발견하고 목표를 향해 계속 진행한다. 다만 목표 자체를 달성할 수 없으면 테스트는 실패한다. 캐시와 재탐색이 실제 실패를 성공으로 바꾸지는 않는다. Keith는 자신의 저장소에서 에이전트에게 토큰 지출을 최대한 줄여 달라고 요청했을 때 에이전트가 테스트를 한 번 실행한 뒤 캐시 파일을 사용하는 것을 확인했고, 단위 테스트처럼 E2E도 품질을 유지하면서 빠르게 실행되는 것이 앱 개발에 큰 이점이라고 본다.

에이전트의 모델, 프롬프트, 도구, 페르소나는 사용자가 정의한다

E2E는 테스트 API이면서 에이전트가 작업을 실행하는 harness다. 사용자는 AI SDK가 지원하는 제공자 중에서 모델과 추론 경로를 고른다. 시연에서는 Vercel AI Gateway로 OpenAI 모델을 썼고, DeepSeek이나 기존 구독을 쓰는 선택지도 언급된다. 에이전트 하나로 고정하지 않고 역할에 맞게 여러 에이전트를 둘 수 있다. 평소에는 기본 에이전트를 쓰고 큰 출시 전에는 Opus 5.5처럼 비싼 모델로 철저히 검토하는 에이전트를 실행하는 식이다.

사용자가 정의하는 것은 모델, 시스템 프롬프트, 컨텍스트, 그리고 도구다. 도구는 AI SDK 도구 형식을 그대로 쓰므로, 장바구니에 초기 데이터를 넣거나 백엔드 상태를 초기화하는 도구를 추가해 E2E가 기본으로 지원하지 않는 앱의 기능을 다루게 할 수 있다. 같은 도구는 MCP를 통해 Claude Code 같은 개발 에이전트에도 노출되므로, 개발 에이전트가 별도의 조작 방식을 다시 구성할 필요가 줄어든다.

페르소나 테스트에서는 행동 캐시를 끄는 편이 맞다. 구매자 페르소나를 정의하고 에이전트가 실행할 때마다 직접 탐색하게 해야 페르소나에 따른 행동을 볼 수 있기 때문이다. Keith는 자신의 야구 퀴즈 게임에 늘 확신을 가지고 답을 고르는 사람과 여기저기 눌러 보며 망설이는 사람을 페르소나로 넣고 싶어 한다. Oskar는 실제 사용자 분석 자료를 Claude에게 읽혀 전형적인 사용자 유형을 추출하고 에이전트가 그 사용자처럼 행동하게 하는 방법을 제안한다. 에이전트의 실행 방식 자체를 바꾸고 싶은 사용자는 컨텍스트 관리와 도구 호출까지 직접 구현할 수 있다.

탐색 에이전트로 버그 배시를 하되, 보고는 증거로 거른다

E2E explorer는 목표를 받은 에이전트가 앱을 자유롭게 탐색하는 명령이다. Oskar는 TesterArmy 자체 개발에서 기능 구현을 마치고 PR을 만들기 전에 버그 배시 스킬을 실행한다. 주 개발 세션이 탐색 에이전트 10개를 띄워 각각 다른 관점에서 기능을 망가뜨려 보게 한다. 하나는 결제 과정에서 잘못된 경로를 따라가고, 하나는 장바구니에 1,000개를 넣으려 하고, 주 세션은 정상 경로를 따르는 사용자 역할을 맡는다. Keith는 이것을 맥주 한 잔, 0잔, 99,999잔, 맥주잔 속 도마뱀, 마이너스 한 잔을 주문하는 테스터 농담에 비유한다.

expo-testerarmy-agentic-ci-e2e-23-39
주 개발 세션이 여러 E2E explorer를 조정하고 설정에서 에이전트를 고르는 버그 배시 안내

시청자의 질문. 다른 앱의 탐색 테스트가 실제 버그가 아닌 것을 보고한 적이 있다. 오탐은 어떻게 피하는가. Oskar는 에이전트에게 단순히 버그를 찾으라고 하면 없는 버그를 만들어 보고할 수 있다고 인정한다. 그의 작업 방식에서는 Claude Code나 Codex 같은 주 개발 에이전트가 조정자 역할을 하고, 탐색 에이전트들은 화면 녹화와 스크린샷 같은 산출물을 결과와 함께 돌려준다. 조정자는 이것이 버그라는 설명을 그대로 받아들이지 않고 증거를 분석한 뒤 사람에게 전달한다. 오탐이 원천적으로 사라지는 것은 아니고, 증거 검토를 한 단계 더 두어 보고의 신뢰도를 높이는 방식이다.

비결정성은 줄일 수 있지만 없앨 수는 없다

시청자의 질문. AI 모델이 비결정적인데 assertion의 일관성은 어떻게 보장하는가. Oskar는 먼저 행동과 판정을 구분한다. 행동인 act는 기본적으로 캐시되며 사용자가 이 캐시를 끌 수 있지만, assertion은 캐시되지 않는다. 에이전트를 정의하는 방식과 도구와 컨텍스트를 사용자가 통제하므로 불안정성을 상당 부분 줄일 수 있지만, 불안정해질 위험이 완전히 없어지지는 않는다. 모델이 오늘 통과라고 판정한 것을 내일 실패로 판단할 수 있고, Oskar는 이런 일을 TesterArmy를 만들면서 실제로 겪었으며, 모델 제공자 서버 쪽에서 생기는 작은 차이로도 판정에 영향이 나타났다고 말한다. 특정 판정이 불안정하면 그 판정을 완전히 결정적인 assertion으로 바꾸면 된다. 과제가 작고 명확하면 불안정하지 않을 것이라고 그는 생각하지만, 보장되는 성질이라고 말하지는 않는다. Oskar는 더 안정적인 응답을 기대하며 자막에서 Jeff로 표기된 system-one 계열 모델도 살펴보고 있다.

Oskar는 CAPTCHA와 OTP 같은 인증 장벽에 대한 질문에 실행 환경에 따라 달라진다고 답한다. CAPTCHA는 Kernel 같은 브라우저 제공자를 사용해 처리할 수 있지만, 대부분의 E2E 테스트는 CAPTCHA 표시 여부를 개발자가 통제하는 로컬 환경에서 실행하는 편이 적절하다. 에이전트가 CAPTCHA 해결을 기다리게 하는 것은 시간 낭비이기 때문이다. OTP는 당시 TesterArmy에서는 지원했지만 오픈소스 E2E에는 아직 없었고, 이메일 제공자와 동적 전화번호 제공자를 통합하는 작업이 더 필요했다.

플랫폼은 엔진으로 분리되고, 같은 테스트가 웹과 iOS와 Android에서 실행된다

E2E의 코어는 특정 플랫폼에 종속되지 않는다. 브라우저나 모바일 기기를 다루는 부분은 엔진으로 분리되고, 웹을 포함한 모든 플랫폼이 out-of-tree다. React Native에서는 Android와 iOS 외의 플랫폼이 지원 수준이 낮아 어려움을 겪는데, 이를 Oskar가 React Native VisionOS 작업에서 직접 본 경험이 이 설계에 영향을 줬다. 엔진 계약은 플랫폼별로 구현할 추상 메서드를 정의하므로 게임 테스트용 엔진이나 새 기기용 엔진을 커뮤니티가 만들 수 있다. Oskar는 기기와 백엔드를 모킹해 빠르게 실행되는 엔진도 구상하고 있지만, 제대로 된 E2E 검사가 더 낫다는 조건을 단다.

테스트는 공통 API로 작성하면 크로스플랫폼으로 유지된다. 플랫폼 전용 기능이 필요할 때만 해당 기능을 엔진 패키지에서 가져온다. 모바일 패키지의 device로는 키체인 처리와 앱 종료와 키보드 닫기 같은 네이티브 기능에 접근하고, 웹 엔진의 browser로는 쿠키와 viewport에 접근한다.

expo-testerarmy-agentic-ci-e2e-34-17
공통 테스트 유지와 엔진 패키지의 device·browser 기능 가져오기 선택지, 그 실행 스크립트

같은 테스트를 웹, iOS, Android에서 차례로 실행한 시연에서 웹과 iOS 실행의 비용은 아래 표와 같이 표시됐다. Android 실행의 비용은 자막에서 단위와 소수점을 확인할 수 없었다. 행동 캐시가 재생되어도 에이전트 assertion은 매번 수행되므로 비용이 발생하고, 캐시를 끄면 모델의 사고 과정과 토큰 사용이 그대로 나타난다. 결과 화면의 75% 캐시는 E2E의 행동 재생 캐시가 아니라 모델 제공자 또는 게이트웨이의 캐시 비율이다. 플랫폼별 행동 캐시는 별개여서 웹에서 만든 캐시를 iOS에 그대로 쓸 수는 없다.

실행 조건 표시된 비용
웹 행동 캐시 재생, 에이전트 assertion 수행 0.0004달러
웹 행동 캐시 끔, 19.5k 토큰, 제공자 캐시 75% 0.0016달러
iOS 클라우드 시뮬레이터 대여, 실행, 해제 포함 0.0077달러
expo-testerarmy-agentic-ci-e2e-37-01
19.5k 토큰과 제공자 캐시 75%를 표시한 결과 화면: 행동 재생 캐시와 모델 제공자 캐시의 구분

비용 표시가 모든 연결 방식에서 똑같이 나오지는 않는다. Vercel AI Gateway는 실제 토큰 사용량과 비용을 반환하지만, 구독을 쓰는 경우에는 비용이 반환되지 않을 수 있다. 캐시 없이 실행해도 비용이 낮았던 것은 사용한 Luna 모델이 저렴했고 harness도 효율적으로 최적화했기 때문이라고 Oskar는 덧붙인다. 이 구간의 모델명은 자막 표기를 따른다.

CI에서는 EAS 클라우드 시뮬레이터로 병렬 실행한다

EAS Workflows에서는 이미 macOS runner 안의 시뮬레이터를 대상으로 E2E 테스트를 실행할 수 있다. 발표 당시 대기 명단 단계였던 EAS Simulators는 EAS 인프라에서 클라우드 시뮬레이터를 실행하는 서비스로, PR 커밋으로 워크플로를 시작하고 테스트를 시뮬레이터 서너 대에 동시에 분배하도록 구성할 수 있다. 설정 한 줄을 바꾸고 EAS Build ID를 제공하고 EAS CLI에 로그인하면 iOS 테스트를 실행할 때 클라우드 시뮬레이터가 시작되고 앱이 설치된다. 로컬 컴퓨터가 macOS가 아니어도 iOS 테스트를 실행할 수 있다. Expo 대시보드에서는 테스트의 모든 행동과 녹화 영상을 볼 수 있다.

expo-testerarmy-agentic-ci-e2e-45-51
iOS 클라우드 시뮬레이터 대여, 테스트 실행, 해제 과정과 비용 결과

클라우드로 옮기는 이유는 로컬 장비의 한계다. Keith는 오래된 ThinkPad에 Linux를 설치하고 저장소 다섯 개를 동시에 열어 작업하다가, 한 에이전트에 E2E 설정을 맡겼더니 에이전트가 지시하지 않은 Android 빌드와 에뮬레이터 실행까지 시작했고, 그 결과 RAM이 모두 소진되어 개발 도구가 종료된 경험을 든다. 그는 이것을 E2E나 도구의 결함으로 보지 않는다. Oskar는 에이전트 다섯 개가 각자 기능을 구현하고 각각이 다시 탐색 에이전트를 시뮬레이터에서 실행하는 상황을 든다. 이는 Mac mini 한 대가 감당하기 어려운 부하를 클라우드로 옮겨 기능별 버그 배시를 수행하는 방식이다. Keith는 여러 사람이 한 방에서 기기를 조작하며 버그를 찾던 버그 배시를 에이전트들이 되살리는 셈이라고 말한다.

기존 테스트 이전은 에이전트가 하고, 활용은 테스트 밖으로 넓어진다

Maestro, Detox, Playwright에서 E2E로 옮기는 프레임워크별 이전 문서가 있다. 기존 API와의 차이, 대체 방법, 변경 전후 예시, API 대응표가 들어 있고, 에이전트에 전달할 프롬프트를 복사할 수 있다. 공개된 지 며칠 만에 이전을 끝낸 사용자들이 있었다. Keith는 많은 테스트가 이미 arrange-act-assert 구조이므로 그 구조를 유지하면서 원하는 정도의 확실성을 고르면 되고, 대응 API로 변환하는 일은 LLM이 잘 처리하는 작업이라고 본다. 에이전트가 로컬에서 테스트를 실행해 이전 작업 전후에 같은 항목을 검사하는지 확인할 수 있으며, Playwright에서 옮기다가 예상하지 못한 버그를 발견해 이전을 위한 PR에 수정까지 넣은 사용자도 있었다. 다만 이전한다고 해서 반드시 새 버그가 발견되는 것은 아니다.

예상하지 못한 활용 사례로 Oskar는 앱스토어 출시용 스크린샷을 E2E로 화면을 조작해 만드는 스킬, 앱을 Apple의 디자인 가이드라인과 심사 가이드라인에 대조하는 출시 전 검사, 앱 실행 중 백엔드 응답이 스키마와 일치하는지 검사하는 백엔드 테스트를 든다. Keith는 앱과 가이드라인이 서로 다른 주기로 바뀌는 상황에서 일부 비결정적 판단을 테스트에 넣으면 테스트를 계속 유효하게 유지하기 쉬울 수 있다고 기대한다. Oskar는 iOS 시뮬레이터만 쓰는 Expo 개발자가 출시 전에 Android도 E2E로 확인하는 교차 검사를 제안하고, 사용자가 에이전트와 엔진을 더 만들기를 기대한다. 이미 컴퓨터 사용을 위한 엔진 하나를 보았다고 한다.

이 대담에서 아직 확인되지 않은 채 남은 것은 셋이다. 에이전트 assertion이 모델 제공자의 변화에도 안정적으로 유지되는지 여부, OTP 통합이 오픈소스 E2E에 들어오는 시점, 그리고 EAS Simulators가 대기 명단 단계를 벗어나 정식 제공되는 시점이다. Oskar가 앞으로 추진하겠다고 말한 통합 확대와 안정성 개선은 발표 당시에 밝힌 계획일 뿐, 완료된 기능 목록이 아니다.