코드 작성 후 단위 테스트를 사용하면 코드의 품질을 확인할 수 있으며 개발을 진행하면서 개발 프로세스를 개선하는 데도 단위 테스트를 사용할 수 있습니다. 애플리케이션 개발을 마친 후에 테스트를 작성하는 대신 개발을 진행하면서 테스트를 작성하는 것이 좋습니다. 그러면 작고 유지관리 및 재사용이 가능한 코드 단위를 설계하는 데 도움이 됩니다. 또한 코드를 완벽하고 신속하게 테스트하기도 더 쉽습니다.
로컬 단위 테스트를 수행할 때는 원격 구성요소를 사용하지 않고 자체 개발 환경 내에서 테스트를 실행합니다. App Engine에서는 데이터 저장소와 기타 App Engine 서비스의 로컬 구현을 사용하는 테스트 유틸리티를 제공합니다. 따라서 코드를 App Engine에 배포하지 않고 서비스 스텁을 사용하여 코드에서 이러한 서비스를 사용하는 방식을 로컬에서 테스트해 볼 수 있습니다.
서비스 스텁은 서비스의 동작을 시뮬레이션하는 수단입니다. 예를 들어 Datastore 및 Memcache 테스트 작성에 표시된 데이터 저장소 서비스 스텁을 사용하면 실제 데이터 저장소에 어떠한 요청도 하지 않고 데이터 저장소 코드를 테스트할 수 있습니다. 데이터 저장소 단위 테스트 중에 저장된 모든 항목은 데이터 저장소가 아니라 메모리에 보관되었다가 테스트 실행 후에 삭제됩니다. 데이터 저장소 자체에 대한 어떠한 종속성도 없이 소규모 테스트를 빠르게 실행할 수 있습니다.
이 문서에서는 테스트 프레임워크 설정에 대한 정보를 제공한 다음, 여러 로컬 App Engine 서비스에 대한 단위 테스트를 작성하는 방법을 설명합니다.
테스트 프레임워크 설정
SDK의 테스트 유틸리티는 특정 프레임워크에 한정되어 있지 않지만 구체적이고 완전한 예시를 위해 이 가이드에서는 예시에서 JUnit을 사용합니다. 테스트 작성을 시작하기 전에 올바른 JUnit 4 JAR을 테스트 클래스 경로에 추가해야 합니다. 이를 완료한 후 간단한 JUnit 테스트를 작성할 수 있습니다.
Eclipse를 실행 중인 경우 실행할 테스트의 소스 파일을 선택합니다. Run(실행) 메뉴 > Run As(다음으로 실행) > JUnit Test(JUnit 테스트)를 선택합니다. 테스트 결과는 Console 창에 나타납니다.
자바 8 테스트 유틸리티 소개
MyFirstTest
는 가장 간단한 테스트 설정을 보여줍니다. App Engine API 또는 로컬 서비스 구현에 의존하지 않는 테스트의 경우 별도의 작업이 필요하지 않을 수도 있습니다. 그러나 테스트 또는 테스트 중인 코드에 이러한 종속 항목이 있는 경우 테스트 클래스 경로에 다음 JAR 파일을 추가합니다.
${SDK_ROOT}/lib/impl/appengine-api.jar
${SDK_ROOT}/lib/impl/appengine-api-stubs.jar
${SDK_ROOT}/lib/appengine-tools-api.jar
이러한 JAR 파일은 런타임 API와 해당 API의 로컬 구현을 테스트에 사용할 수 있도록 합니다.
App Engine 서비스는 실행 환경에서 여러 가지 일을 기대하며 이러한 것들을 설정하려면 상당한 양의 상용구 코드가 필요합니다. 이를 직접 설정하는 대신 com.google.appengine.tools.development.testing
패키지의 유틸리티를 사용할 수 있습니다. 이 패키지를 사용하려면 테스트 클래스 경로에 다음 JAR 파일을 추가합니다.
${SDK_ROOT}/lib/testing/appengine-testing.jar
잠시 시간을 내어 com.google.appengine.tools.development.testing
패키지의 javadoc를 살펴보세요. 이 패키지의 가장 중요한 클래스는 LocalServiceTestHelper로, 필요한 모든 환경 설정을 처리하며 테스트에서 액세스하려는 모든 로컬 서비스에 대한 최상위 수준의 구성 지점을 제공합니다.
특정 로컬 서비스에 액세스하는 테스트를 작성하려면 다음을 수행합니다.
- 특정 로컬 서비스에 대한
LocalServiceTestConfig
구현으로LocalServiceTestHelper
인스턴스를 만듭니다. - 각 테스트 전에
LocalServiceTestHelper
인스턴스에서setUp()
을 호출하고 각 테스트 후에tearDown()
을 호출합니다.
Datastore 및 Memcache 테스트 작성
다음 예는 datastore 서비스의 사용을 테스트합니다.
이 예시에서 LocalServiceTestHelper
는 모든 로컬 서비스에 공통된 실행 환경 부분을 설정 및 해제하며 LocalDatastoreServiceTestConfig
는 로컬 Datastore 서비스에만 해당하는 실행 환경 부분을 설정 및 해제합니다. javadoc에서 설명한 대로 이를 위해서는 모든 데이터를 메모리에 유지하도록(정기적으로 디스크로 내보내는 대신) 로컬 Datastore 서비스를 구성하고 테스트가 끝날 때마다 메모리의 모든 데이터를 완전 삭제해야 합니다. 이는 Datastore 테스트의 기본 동작에 불과하며 원하지 않는 경우 변경할 수 있습니다.
Datastore 대신 Memcache에 액세스하도록 예시 변경
로컬 memcache 서비스에 액세스하는 테스트를 만들려면 위의 코드를 약간만 변경하면 됩니다.
Datastore와 관련된 클래스를 가져오는 대신 Memcache와 관련된 클래스를 가져오면 됩니다. LocalServiceTestHelper
는 동일하게 가져와야 합니다.
생성 중인 클래스의 이름을 변경하고 Memcache와 관련되도록 LocalServiceTestHelper
의 인스턴스를 변경합니다.
마지막으로 Memcache와 관련되도록 테스트의 실제 실행 방식을 변경합니다.
Datastore 예시와 같이 LocalServiceTestHelper
와 서비스별 LocalServiceTestConfig
(이 경우 LocalMemcacheServiceTestConfig
)는 실행 환경을 관리합니다.
Cloud Datastore 테스트 작성
앱이 Cloud Datastore를 사용하는 경우 eventual consistency에 직면하여 애플리케이션의 동작을 확인하는 테스트를 작성할 수 있습니다. LocalDatastoreServiceTestConfig
는 이를 쉽게 하는 옵션을 제공합니다.
적용되지 않은 작업 비율을 100으로 설정하면 로컬 데이터 저장소가 최대한의 최종 일관성 상태에서 작동하게 됩니다. 최대한의 최종 일관성 상태에서는 쓰기가 커밋되지만 항상 적용에 실패하므로 전역(비상위) 쿼리가 지속적으로 변경사항을 확인하지 못하게 됩니다. 물론 이것이 프로덕션 환경에서 실행 시 애플리케이션에서 실제로 확인되는 eventual consistency 수준을 나타내지는 않지만 테스트 목적으로는 로컬 Datastore를 매번 이런 식으로 작동하도록 구성하는 것이 매우 유용합니다.
적용할 수 없는 트랜잭션을 보다 세밀하게 제어하려면 자체 HighRepJobPolicy
를 등록하세요.
Testing API는 eventual consistency에 직면하여 애플리케이션이 제대로 작동하는지 확인하는 데 유용하지만, 로컬 고성능 복제 읽기 일관성 모델은 프로덕션 고성능 복제 읽기 일관성 모델의 정확한 복제본이 아니라 근사치임에 유의해야 합니다. 로컬 환경에서 적용되지 않은 쓰기가 있는 항목 그룹에 속하는 Entity
에 대해 get()
을 수행하면 후속 전역 쿼리에서는 적용되지 않은 쓰기의 결과가 항상 표시됩니다. 이는 프로덕션 환경에서는 해당되지 않습니다.
작업 대기열 테스트 작성
로컬 작업 대기열을 사용하는 테스트는 데이터 저장소 및 memcache와 달리 작업 대기열 API가 서비스 상태를 검사하는 기능을 제공하지 않으므로 좀 더 복잡합니다. 예상한 매개변수로 태스크가 예약되었는지 확인하려면 로컬 태스크 큐 자체에 액세스해야 합니다. 이를 위해 com.google.appengine.api.taskqueue.dev.LocalTaskQueue
가 필요합니다.
LocalTaskqueueTestConfig
에 로컬 서비스 인스턴스에 대한 핸들을 요청한 다음 로컬 서비스 자체를 조사하여 태스크가 예상대로 예약되었는지 확인합니다. 모든 LocalServiceTestConfig
구현은 비슷한 메서드를 제공합니다. 항상 필요한 것은 아니지만 필요한 순간에 유용하게 사용할 수 있습니다.
queue.xml
구성 파일 설정
태스크 큐 테스트 라이브러리를 사용하면 LocalTaskQueueTestConfig.setQueueXmlPath
메서드를 통해 LocalServiceTestHelper별로 queue.xml
구성을 원하는 만큼 지정할 수 있습니다. 현재 로컬 개발 서버는 모든 큐의 비율 한도 설정을 무시합니다.
로컬에서는 태스크를 동시에 실행할 수 없습니다.
예를 들어 App Engine 애플리케이션에서 업로드하여 사용할 queue.xml
파일을 프로젝트에서 테스트해야 할 수 있습니다. queue.xml
파일이 표준 위치에 있다고 가정하면 위의 샘플 코드를 다음과 같이 수정하여 src/main/webapp/WEB-INF/queue.xml
파일에 지정된 큐에 대한 테스트 액세스 권한을 부여할 수 있습니다.
프로젝트의 파일 구조에 맞게 queue.xml
파일 경로를 수정합니다.
QueueFactory.getQueue
메서드를 사용하여 이름으로 큐에 액세스합니다.
지연된 태스크 테스트 작성
애플리케이션 코드에서 지연된 작업을 사용하는 경우 자바 테스트 유틸리티를 사용하면 이러한 작업의 결과를 확인하는 통합 테스트를 쉽게 작성할 수 있습니다.
첫 번째 로컬 태스크 큐 예시와 마찬가지로 LocalTaskqueueTestConfig
를 사용하되 이번에는 태스크가 예약되었는지 여부뿐만 아니라 실행되었는지 여부도 손쉽게 확인할 수 있는 몇 가지 추가 인수로 이를 초기화합니다. 여기서는 setDisableAutoTaskExecution(false)
을 호출하여 자동으로 태스크를 실행하도록 로컬 태스크 큐에 알립니다. setCallbackClass(LocalTaskQueueTestConfig.DeferredTaskCallback.class)
를 호출하여 지연된 태스크를 실행하는 방법을 이해하는 콜백을 사용하도록 로컬 태스크 큐에 알립니다. 마지막으로 setTaskExecutionLatch(latch)
를 호출하여 각 태스크가 실행된 후 래치를 감소하도록 로컬 태스크 큐에 알립니다. 이러한 구성을 통해 지연된 태스크를 큐에 추가하고 해당 태스크가 실행될 때까지 기다린 다음 태스크가 실행될 때 예상대로 작동하는지 확인하는 테스트를 작성할 수 있습니다.
로컬 서비스 기능 테스트 작성
기능 테스트는 데이터 저장소, blobstore, memcache 등과 같은 일부 서비스의 상태를 변경하고 해당 서비스에 대해 애플리케이션을 실행하여 애플리케이션이 다른 조건에서 예상대로 응답하는지 확인합니다. 기능 상태는 LocalCapabilitiesServiceTestConfig 클래스를 사용하여 변경할 수 있습니다.
다음 코드 스니펫은 데이터 저장소 서비스의 기능 상태를 사용 중지로 변경한 다음 데이터 저장소 서비스에 대한 테스트를 실행합니다. 필요에 따라 데이터 저장소를 다른 서비스로 대체할 수 있습니다.
샘플 테스트는 먼저 Datastore로 초기화된 Capability
객체를 만든 다음 사용 중지로 설정된 CapabilityStatus
객체를 생성합니다. LocalCapabilitiesServiceTestConfig
는 앞서 생성된 Capability
및 CapabilityStatus
객체를 사용하여 기능 및 상태 집합으로 생성됩니다.
그런 다음 LocalCapabilitiesServiceTestConfig
객체를 사용하여 LocalServiceHelper
가 생성됩니다. 테스트가 설정되었으니 이제 DatastoreService
가 만들어지며 테스트가 예상된 결과(이 경우 CapabilityDisabledException
)를 생성하는지 확인하기 위해 쿼리가 전송됩니다.
다른 서비스에 대한 테스트 작성
blobstore와 기타 App Engine 서비스에서 테스트 유틸리티를 사용할 수 있습니다. 테스트용 로컬 구현이 있는 모든 서비스 목록은 LocalServiceTestConfig
문서를 참조하세요.
인증 기대치가 있는 테스트 작성
이 예시에서는 UserService를 사용하여 사용자가 로그온했는지 또는 관리자 권한이 있는지 확인하는 논리를 검증하는 테스트를 작성하는 방법을 보여줍니다. 기본 역할인 뷰어, 편집자 또는 소유자이거나 사전 정의된 역할인 App Engine 앱 관리자에 해당하는 모든 사용자는 관리자 권한을 갖습니다.
이 예시에서는 LocalUserServiceTestConfig
로 LocalServiceTestHelper
를 구성하므로 테스트에서 UserService
를 사용할 수 있지만 LocalServiceTestHelper
자체에도 일부 인증 관련 환경 데이터를 구성합니다.
이 예시에서는 OAuthService
를 사용할 수 있도록 LocalUserServiceTestConfig
로 LocalServiceTestHelper
를 구성합니다.