루니버스에서 테스트 하는 방법 — 1편 — 소프트웨어 테스트란?

Randel
Luniverse
Published in
7 min readJan 3, 2020
Tech Luniverse

소프트웨어 테스트란 무엇일까요? 한 문장으로 “개발된 기능이 기획의 요구사항에 맞게 동작하는지 확인하는 것”으로 설명할 수 있습니다. 그리고 이 목적을 이루기 위해 개발자는 다양한 종류의 테스트 코드를 작성합니다. 예를 들면 단위 테스트, 통합 테스트 및 시스템 테스트 등이 있습니다. 각각의 테스트에 대한 정의, 목적 및 수행하는 방법이 다르므로 주어진 상황에 맞는 테스트를 선택해서 테스트 코드를 작성해야 합니다.

Software Test의 종류

  1. Unit Test
  2. Integration Test
  3. System Test
Software Tests

가장 많이 사용되는 소프트웨어 테스트인 Unit Test, Integration Test, System Test에 대해 알아보겠습니다.

테스트 환경 및 라이브러리

각각의 테스트를 설명하기 위해 테스트 코드가 포함되어 있습니다. 루니버스에서 사용하는 테스트 코드 환경과 라이브러리는 아래 리스트를 참조하시면 됩니다.

  • node.js
  • nyc
  • mocha
  • sinon
  • chai

1) Unit Test

Unit Test는 각각의 독립적인 모듈을 테스트하기 위해서 주로 내부 구조와 동작을 검사하는 화이트 박스 테스트로 작성됩니다. 예를 들어 특정 함수가 호출 되었을 때 개발자가 의도한 대로 함수가 동작 하는지를 확인하기 위해 사용할 수 있습니다. Unit Test는 테스트하는 모듈에만 관심이 있기 때문에 외적으로 Dependency가 있는 코드들은 모두 Mock을 주입합니다. 아래 코드를 예로 들어보겠습니다.

*화이트 박스 테스트란?

소프트웨어 내부 소스코드의 동작을 개발자가 추적하여, 동작의 유효성 및 실행과정을 살펴볼 수 있어 테스트를 통과하지 못하거나 불필요하게 사용 되는 코드의 흐름을 확인할 수 있다.

API FOR Queries

위 API는 특정 계정의 정보를 반환합니다. 먼저 API가 호출되면 API를 요청한 session이 유효한지 판별하고 유효하면 session을 저장합니다. 그다음 조회할 계정의 accountId를 uri path에서 가져옵니다. 본인 계정의 정보만 조회할 수 있기 때문에 API를 요청한 session의 accountId와 조회할 계정의 accountId가 같은지 검사합니다. 같다면 데이터베이스에서 accountId로 계정을 조회하고 반환합니다.

API

이제 Unit Test 코드를 살펴봅시다. 먼저 외부 Dependency 코드에 Mock을 주입할 때 사용할 변수를 미리 정의합니다. 그다음 session이 유효한지 검증하는 getSession메서드에 stub을 주입합니다. 이제 getSession 메서드가 호출되면 실제 구현체와 상관없이 주입된 Mock 데이터만 반환(return) 하게 됩니다. 그다음 문자열 “accountId” 를 인자로 받았을 때만 getUriParam 메서드의 동작을 변형시키는 stub을 주입합니다. 이제 getUriParam 메서드를 호출할 때 문자열 “accountId”를 파라미터로 넣으면 구현체와 상관없이 미리 정의된 accountId가 return 됩니다. 마지막으로 데이터베이스에서 계정을 조회하는 inquiry 메서드에 stub을 주입합니다.

이제 테스트할 메서드를 수행합니다. 수행하고 나면 메서드가 개발자의 의도대로 동작했는지 확인합니다. 먼저 session이 유효한지 검증하는 getSession 메서드가 한 번만 호출 됐는지 확인합니다. 그다음 uri path에서 accountId를 받아오는 getUriParam 메서드가 반드시 한 번 호출 됐는지 확인합니다. 이 때 파라미터로 문자열 “accountId”가 포함됐는지도 확인합니다. 그 후 데이터베이스에서 계정을 조회하는 inquiry 메서드가 한 번만 호출 됐는지와 호출할 때 파라미터로 accountId, withInfo가 포함 됐는지도 확인합니다.

위 테스트 코드를 통해 session을 검증하는 로직이 동작하는지, 데이터 베이스에서 올바른 accountId로 계정을 조회하는게 맞는지 검증할 수 있습니다. 외부 Dependency가 있는 코드들을 stub을 통해 자유자재로 조작해서 여러 케이스에 대해 검증하는 테스트 코드를 쉽게 작성할 수 있습니다. 대신 실제 모듈들끼리의 연동은 검증하지 않기 때문에 실제로 연동했을 때 예기치 못한 문제가 발생할 수 있습니다. 이러한 문제를 발견하기 위해 Integration Test 코드를 작성합니다.

2) Integration Test

Unit Test는 각각의 모듈을 테스트하는 것이기 때문에 모듈끼리 통합했을 때 발생하는 결함은 발견할 수 없습니다. 그래서 실제로 모듈들을 통합했을 때 잘못된 결과가 나올 수 있습니다. 루니버스(Luniverse)에서는 이러한 결함을 Integration Test 가 아닌 System Test를 통해 감지하고 있습니다. 두 테스트는 서로 다른 테스트 이지만 겹치는 경우가 많고 두 개의 테스트를 모두 작성할 수 있는 인력이 부족하기 때문에 실제 유저 환경에서 진행하는 System Test 코드를 작성합니다.

3) System Test

System Test는 End to End 테스트라고도 불립니다. 이 테스트는 유저 입장에서 테스트하는 것입니다. 예를 들어 루니버스의 BaaS 플랫폼에서 회원가입을 한 뒤에 로그인을 하고 체인을 만들고 메인 토큰을 만드는 행위들을 실제로 수행해 보는 겁니다.

루니버스 회원가입 바로가기

Go to Luniverse Sign-up

다른 테스트들과는 다르게 Mock같은 것들은 전혀 사용하지 않고 실제로 기능을 수행하는 겁니다. 그렇기 때문에 수행 시간도 굉장히 긴 편입니다. 예를 들어 루니버스 BaaS의 경우 메인토큰 생성 테스트를 하기 위해서 먼저 체인을 생성해야 합니다. 체인의 노드별로 독립적인 instance를 설치하기 위해서몇 분의 시간이 소요됩니다. 또한 특정 모듈의 System Test를 수행하기 앞서 수행해야 할 Dependency들이 많기 때문에 System Test 코드를 처음 짤 때 많은 시간이 소모됩니다. 예를 들어, 체인을 생성 하려면 이메일 인증 ➡️ 회원가입 ➡️ 로그인 순서로 System Test 코드가 먼저 수행되어야 합니다. 시스템 테스트는 Input을 넣고 Output이 올바르게 나오는지만 확인하는 블랙 박스 테스트를 주로 사용합니다. 아래는 System Test 코드입니다. Unit Test때 보았던 계정을 조회하는 API를 테스트합니다.

API

GET_Account 메서드는 실제로 API를 호출하는 메서드 입니다. uri path에 넣을 accountId와 Authorization Header에 넣을 Bearer Token을 인자로 받습니다. 테스트는 굉장히 간단 합니다. Response가 개발자의 의도대로 반환 되었는지만 확인합니다. Status Code가 200인지 확인하고 요청했던 accountId와 반환된 accountId가 동일한지 확인합니다.

그런데 accountId 와 token을 어떻게 얻을 수 있을까요? 위의 System Test 코드를 수행하기 전에 회원가입과 로그인 System Test 코드를 수행해서 얻은 accountId와 token값을 저장해서 사용해야 합니다. 이처럼 System Test는 Dependency가 존재하는 경우가 많아 처음 작성할 때 시간이 많이 걸리는 편입니다. 루니버스(Luniverse)에서는 공통적으로 사용 되는 회원가입, 로그인 등의 System Test는 맨 처음 미리 수행해두고 Response값을 저장해서 다른 System Test 코드에 사용합니다.

앞선 예제와 마찬가지로 테스트 코드를 작성해 놓으면 코드가 정상적으로 동작한다는 것을 검증할 수 있습니다. 신규 기능을 추가하거나 수정할 때 개발자의 실수로 다른 코드에 영향을 미쳐 버그가 생길 수 있는데 테스트 코드가 작성되어 있으면 버그를 사전에 감지해서 수정할 수 있습니다.

2편에서는 루니버스에서 TDD를 활용하는 방법에 대해서 알아보겠습니다.

Luniverse

--

--