Scenario Test: 인수 테스트 자동화로 자신감, 생산성 높이기 (GopherCon 2023 발표)
- 성능, 강타입 언어 등의 이점을 누리기 위해 거대한 레거시 시스템을 Python에서 Go로 마이그레이션 했던 경험을 간단하게 소개합니다.
- 마이그레이션 검증 과정에서 큰 역할을 했던 자동화 테스트 도구 Scenario Tester를 Go로 개발하기 위해 사용했던 전략, 도구 등을 소개합니다.
- 그럼에도 불구하고 발생했던 장애 경험과 개선기를 공유합니다
코드가 많음 - 그래서 잘 안짜게 되거나 생산성이 떨어짐 - Framework이 없다면 각자 다른 스타일로 짜게 됨 - 규칙을 정할 순 있겠으나, 강제하기 어려움 2. 어떤 인수테스트가 있는지 한 눈에 보기 어려움 - 이 서비스에는 어떤 기능적인 요구사항을 만족하고 있을까? 3. 언어 변경을 하게 된다면 사람이 손으로 옮겨줘야 함 - 언어를 변경할 일은 잘 없으니 참을만 함
형태”의 인수 테스트 케이스 하나 - (Given) Context: 이 데이터를 처리하기 위해 필요한 외부 데이터 (RDB 데이터, OS 환경변수 등) - (When) Input: 실제 Component의 Input - (Then) Output: 기대하는 Output (Kafka로 보내야하는 데이터, Cache에 써야하는 데이터 등) my_scenario.json
Scenario를 만듦 - Kafka -> Generator(on ECS) -> Kafka 구조로 생성 - 100억 개가 넘는 케이스를 생성 - 장애 방지를 위해 Scenario Generator 전용 Infra를 다 새로 띄워 격리시킴 2. 만들어진 Scenario로 Golang 코드 검증 - Kafka -> Tester(on ECS) -> Kafka 구조로 테스트 - 실패한 케이스만 모아두고 로컬에서 검증 - 코드 다 고쳤으면 다시 검증 마이그레이션 과정에서의 활용
- “<”와 “>”를 unicode escaping 해버림 - json.Unmarshal을 할 때 case-insensitive하게 받음 단위 테스트로는 못잡았던 케이스 <해결> A. Case Sensitivity가 중요하지 않은 struct(정해진 값만 들어오는 것이 확정)는 Known-issue로 처리 B. Case Sensitivity가 중요한 struct는 정해진 Key만 받도록 Unmarshal을 직접 구현 (별로 행복하지 않음... 😭 더 좋은 방법/라이브러리가 있다면 알려주세요)
- “<”와 “>”를 unicode escaping 해버림 - json.Unmarshal을 할 때 case insensitive하게 받음 2. 그외 미처 생각도 못했던 곳에서 발생했던 실수 - Timestamp를 13자리(ms)로 기록해야하는데 10자리(sec)로 기록함 - 잘못된 hash 알고리즘으로 hashing을 함 단위 테스트로는 못잡았던 케이스 (기억은 다 안나지만, 이거 보다 훨씬 많은 케이스가 있었습니다)
코드를 짜놔야 편하다 - Mock Infra로 쉽게 교체할 수 있어야 Generator/Tester를 만들기 편함 2. 활용한 도구 - https://github.com/yudai/gojsondiff - Diff가 생겼을 때 예쁘게 볼 수 있음 - https://pkg.go.dev/gorm.io/gorm/callbacks#AfterQuery - gorm AfterQuery Callback을 통해 Context를 capture할 수 있음 3. stdin, stdout(+ Makefile)을 잘 활용하면 쉽게 생성/테스트 할 수 있다 만들때 팁
- 고도화를 하다보니, 서비스 하나를 만드는 수준의 공수가 들었음 - 언어마다 다 각자 개발해줘야 함 (Python, Go, Rust) 2. Scenario Generator/Tester를 잘못 만들면 말짱 도루묵 - 일반적으로 짜는 테스트 코드 보다 복잡하기 때문에 실수하기 더 쉬움 단점
Scenario를 잘못 만듦 - Input Data를 코드에서 다루다가 변경해버림 - A -> A’ 로 바뀐 상태로 Scenario가 저장됨 - A’를 Input으로 받는 코드를 작성해서 Golang으로 신나게 검증을 함 🥲 겪었던 장애 - Scenario를 잘못 만든 케이스 Input Data는 불변성을 보장할 수 있도록 장치 추가
하면 오히려 안좋다 ◦ 매몰작업이 될 수 있더라도 미리 리팩토링을 하고, 같은 구조로 옮겨야 검증이 쉬움 • 일정은 더 넉넉히 잡아야 한다 (자기 자신을 과대평가 하지 말자) ◦ 처음에 2개월 정도 걸릴줄 알았으나 4개월 걸림 • 리팩토링을 이제 정말 자신있게 할 수 있게 되었다 ◦ 레거시를 다시 짜면서 성능 뿐만 아니라 오히려 생산성도 올라감 ◦ 하지만 이때 제일 조심해야됨
할 수 있게 하기 - A Service의 Output을 B Service의 Input으로 씀 - 한 서비스의 시나리오 파일은 Downstream 서비스들에 다 포함돼야 함 2. 언어에 구애받지 않는 단일화된 Generator/Tester 만들기 - 현재는 언어마다 각자의 Generator/Tester가 있음 - 관리도 힘들고 사용법도 각자 조금씩 다름 - 잘 만들어서 Open Source화를 할 수 있으면 좋지 않을까 3. 비기능적 요구사항도 테스트