미래 지향적인 웹 앱 구축: The Codest의 전문가 팀이 제공하는 인사이트
The Codest가 최첨단 기술로 확장 가능한 대화형 웹 애플리케이션을 제작하고 모든 플랫폼에서 원활한 사용자 경험을 제공하는 데 탁월한 성능을 발휘하는 방법을 알아보세요. Adobe의 전문성이 어떻게 디지털 혁신과 비즈니스를 촉진하는지 알아보세요...
더 쉽게 테스트할 수 있는 방법을 찾고 계신가요? 저희가 도와드리겠습니다! 다음 글을 확인하시고 어떻게 가능한지 알아보세요.
최신 애플리케이션 개발은 한 가지 간단한 규칙을 기반으로 합니다:
컴포지션 사용
우리는 클래스, 함수, 서비스를 더 큰 소프트웨어 조각으로 구성합니다. 이 마지막 요소는 마이크로서비스의 기초이며 육각형 아키텍처. 기존 솔루션을 사용하고 소프트웨어와 통합한 후 바로 시장.
계정 등록을 처리하고 사용자 데이터를 저장하고 싶으신가요? OAuth 서비스 중 하나를 선택할 수 있습니다. 애플리케이션에서 구독이나 결제를 제공하시나요? 이를 처리하는 데 도움이 되는 많은 서비스가 있습니다. 웹사이트에 대한 분석이 필요하지만 GDPR을 이해하지 못하시나요? 부담 없이 바로 사용할 수 있는 솔루션 중 하나를 선택하세요.
비즈니스 관점에서 보면 개발이 너무 쉬워도 간단한 테스트를 작성해야 하는 순간에는 두통이 생길 수 있습니다.
단위 테스트는 매우 간단합니다. 규칙만 준수하면 테스트 환경과 코드 건강합니다. 어떤 규칙이 있나요?
통합 테스트를 작성하려고 하면 모든 것이 복잡해집니다. 몇 가지 서비스를 함께 테스트하는 것은 나쁘지 않습니다. 하지만 데이터베이스나 메시징 서비스와 같은 외부 리소스를 사용하는 서비스를 테스트해야 할 때는 문제가 발생합니다.
수년 전에는 데이터베이스 등을 통합 테스트하고 사용하고자 할 때 두 가지 옵션이 있었습니다:
둘 다 장점도 있고 단점도 있습니다. 하지만 둘 다 추가적인 복잡성을 야기합니다. 때로는 특정 도구의 특성으로 인해 발생하는 기술적 복잡성(예: 로컬 호스트에 Oracle DB 설치 및 관리)이었습니다. 때로는 테스트에 동의해야 하는 등 프로세스의 불편함이 있었습니다. 팀 테스트를 실행할 때마다 JMS 사용에 대해 설명합니다.
지난 10년 동안 컨테이너화라는 개념은 업계에서 인정을 받았습니다. 따라서 통합 테스트 문제에 대한 해결책으로 컨테이너를 선택하는 것은 당연한 결정이었습니다. 컨테이너는 간단하고 깔끔한 솔루션입니다. 프로세스 빌드를 실행하기만 하면 모든 것이 작동합니다! 믿기지 않으시나요? 이 간단한 maven 빌드 구성을 살펴보세요:
<플러그인
com.dkanejs.maven.plugins
docker-compose-maven-plugin
4.0.0
up
테스트-컴파일
up</goal
${프로젝트.baseir}/docker-compose.yml
true
down
통합 후 테스트
다운</목표
${project.basedir}/docker-compose.yml
true
그리고 docker-compose.yml
파일도 꽤 멋져 보입니다!
버전: "3.5"
서비스:
postgres:
컨테이너_이름: 리액티브B
이미지: postgres:13.2
restart: 항상
환경
- POSTGRES_USER=admin
- POSTGRES_PASSWORD=password
- POSTGRES_DB=cities
포트
- "5432:5432"
volumes:
- postgres_data:/데이터/db
pgadmin:
컨테이너_이름: pgadmin4
이미지: dpage/pgadmin4
restart: 항상
환경
PGADMIN_DEFAULT_EMAIL: [email protected]
PGADMIN_DEFAULT_PASSWORD: 비밀번호
포트
- "15050:80"
볼륨
- pgadmin_data:/data/pgadmin
volumes:
postgres_data:
pgadmin_data:
하지만 여기서 문제를 발견할 수 있을까요?
위의 예는 매우 간단합니다. 포스트그레스 데이터베이스인 pgAdmin 하나만 있으면 됩니다. 실행 시
bash
$ mvn 클린 확인
를 설정하면 maven 플러그인이 컨테이너를 시작하고 테스트가 끝나면 컨테이너를 끕니다. 프로젝트가 커지고 컴포즈 파일도 커지면 문제가 시작됩니다. 매번 모든 컨테이너를 시작해야 하며 전체 빌드 내내 컨테이너가 살아 있을 것입니다. 플러그인 실행 구성을 변경하여 상황을 조금 더 개선할 수 있지만 충분하지 않습니다. 최악의 시나리오에서는 테스트가 시작되기 전에 컨테이너가 시스템 리소스를 모두 소진합니다!
그리고 이것이 유일한 문제는 아닙니다. IDE에서 단일 통합 테스트를 실행할 수 없습니다. 그 전에는 컨테이너를 직접 시작해야 합니다. 또한, 다음번 maven을 실행하면 해당 컨테이너가 해체됩니다( down
실행).
따라서 이 솔루션은 대형 화물선과 같습니다. 모든 것이 잘 작동하면 괜찮습니다. 예기치 않거나 흔하지 않은 행동은 우리를 일종의 재앙으로 이끌 수 있습니다.
하지만 테스트에서 컨테이너를 실행할 수 있다면 어떨까요? 이 아이디어는 좋아 보이며 이미 구현되고 있습니다. 테스트 컨테이너이 프로젝트에 대해 이야기하고 있기 때문에 여기에 우리의 문제에 대한 해결책이 있습니다. 이상적이지는 않지만 완벽한 사람은 없습니다.
이것은 Java 라이브러리를 사용하여 가볍고 쉽게 Docker 컨테이너를 실행할 수 있는 방법을 제공합니다. 이를 살펴보고 코드를 작성해 보겠습니다!
시작하기 전에 구성을 확인해야 합니다. 테스트 컨테이너 필요:
특정 OS 및 CI에 대한 요구 사항에 대한 자세한 내용은 다음에서 확인할 수 있습니다.
in 문서.
이제 다음에 몇 줄을 추가할 차례입니다. pom.xml
.
org.testcontainers
testcontainers-bom
${testcontaines.version}
pom
import
org.postgresql
postgresql
runtime
org.testcontainers
postgresql
test
org.testcontainers
junit-jupiter
test
사용 테스트 컨테이너 버전 1.17.3
를 사용해야 하지만 최신 버전을 자유롭게 사용하세요.
첫 번째 단계는 컨테이너 인스턴스를 준비하는 것입니다. 테스트에서 직접 할 수도 있지만 독립 클래스가 더 좋아 보입니다.
public class Postgres13TC extends PostgreSQLContainer {
비공개 정적 최종 Postgres13TC TC = new Postgres13TC();
private Postgres13TC() {
super("postgres:13.2");
}
public static Postgres13TC getInstance() {
TC를 반환합니다;
}
@Override
public void start() {
super.start();
System.setProperty("DB_URL", TC.getJdbcUrl());
System.setProperty("DB_USERNAME", TC.getUsername());
System.setProperty("DB_PASSWORD", TC.getPassword());
}
@Override
public void stop() {
// 아무것도 하지 않습니다. 이것은 공유 인스턴스입니다. 이 작업은 JVM이 처리하도록 합니다.
}
}
테스트를 시작할 때 다음과 같은 인스턴스를 생성합니다. Postgres13TC
. 이 클래스는 컨테이너에 대한 정보를 처리할 수 있습니다. 여기서 가장 중요한 것은 데이터베이스 연결 문자열과 자격 증명입니다. 이제 아주 간단한 테스트를 작성해 보겠습니다.
테스트 컨테이너
SimpleDbTest 클래스 {
@Container
비공개 정적 Postgres13TC = Postgres13TC.getInstance();
@Test
void testConnection() {
assumeThat(postgres13TC.isRunning());
var connectionProps = 새로운 속성();
connectionProps.put("user", postgres13TC.getUsername());
connectionProps.put("password", postgres13TC.getPassword());
try (Connection = DriverManager.getConnection(postgres13TC.getJdbcUrl(),
connectionProps)) {
var resultSet = connection.prepareStatement("Select 1").executeQuery();
resultSet.next();
assertThat(resultSet.getInt(1)).isEqualTo(1);
} catch (SQLException sqlException) {
assertThat((Exception) sqlException).doesNotThrowAnyException();
}
}
}
여기서는 JUnit 5를 사용합니다. 주석 테스트 컨테이너
는 테스트 환경에서 컨테이너를 제어하는 확장 기능의 일부입니다. 이들은 모든 필드에 @Container
어노테이션과 시작 및 중지 컨테이너를 각각 설정합니다.
앞서 언급했듯이 저는 프로젝트에서 Spring Boot를 사용합니다. 이 경우 코드를 조금 더 작성해야 합니다. 첫 번째 단계는 추가 구성 클래스를 만드는 것입니다.
@Slf4j
공용 클래스 ContainerInit 구현
ApplicationContextInitializer {
public static Postgres13TC;
static {
postgres13TC = Postgres13TC.getInstance();
postgres13TC.start();
}
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(
applicationContext,
"spring.datasource.url=" + postgres13TC.getJdbcUrl(),
"spring.datasource.username=" + postgres13TC.getUsername(),
"spring.datasource.password=" + postgres13TC.getPassword(),
"db.host=" + postgres13TC.getHost(),
"db.port=" + postgres13TC.getMappedPort(postgres13TC.POSTGRESQL_PORT),
"db.name=" + postgres13TC.getDatabaseName(),
"db.username=" + postgres13TC.getUsername(),
"db.password=" + postgres13TC.getPassword()
);
}
}
이 클래스는 기존 프로퍼티를 다음과 같은 값으로 재정의합니다. 테스트 컨테이너. 처음 세 개의 프로퍼티는 표준 Spring 프로퍼티입니다. 다음 5개는 추가 사용자 정의 프로퍼티로, 예를 들어 liquibase와 같은 다른 리소스 및 확장을 구성하는 데 사용할 수 있습니다:
spring.liquibase.change-log=classpath:/db/changelog/dbchangelog.xml
spring.liquibase.url=jdbc:postgresql://${db.host:localhost}:${db.port:5432}/${db.name:cities}
spring.liquibase.user=${db.username:admin}
spring.liquibase.password=${db.password:password}
spring.liquibase.enabled=true
이제 간단한 통합 테스트를 정의할 차례입니다.
스프링부트테스트(웹환경 = RANDOM_PORT)
자동 구성 테스트 데이터베이스(대체 = NONE)
컨텍스트 구성(초기화자 = ContainerInit.class)
테스트 컨테이너
클래스 더미 리포지토리 테스트 {
@Autowired
비공개 DummyRepository;
@Test
void shouldReturnDummy() {
var byId = dummyRepository.getById(10L);
var expected = new Dummy();
expected.setId(10L);
assertThat(byId).completes().emitsCount(1).emits(expected);
}
}
여기에 몇 가지 추가 주석이 있습니다.
스프링부트테스트(웹환경 = RANDOM_PORT)
- 는 테스트를 Spring Boot 테스트로 표시하고 스프링 컨텍스트를 시작합니다.자동 구성 테스트 데이터베이스(대체 = NONE)
- 이 주석은 스프링 테스트 확장이 메모리 구성에서 포스트그레스 데이터베이스 구성을 H2로 대체해서는 안 된다고 말합니다.컨텍스트 구성(초기화자 = ContainerInit.class)
- 추가 스프링 컨텍스트테스트 컨테이너
- 앞서 언급했듯이 이 어노테이션은 컨테이너 수명 주기를 제어합니다.이 예에서는 리액티브 리포지토리를 사용하지만 일반적인 JDBC 및 JPA 리포지토리에서도 동일하게 작동합니다.
이제 이 테스트를 실행할 수 있습니다. 처음 실행하는 경우 엔진이 docker.hub에서 이미지를 가져와야 합니다. 잠시 시간이 걸릴 수 있습니다. 그 후 두 개의 컨테이너가 실행된 것을 볼 수 있습니다. 하나는 포스트그레스이고 다른 하나는 테스트컨테이너 컨트롤러입니다. 두 번째 컨테이너는 실행 중인 컨테이너를 관리하며, JVM이 예기치 않게 중지되더라도 컨테이너를 끄고 환경을 정리합니다.
테스트 컨테이너 는 Docker 컨테이너를 사용하는 통합 테스트를 만드는 데 도움이 되는 매우 사용하기 쉬운 도구입니다. 이를 통해 유연성이 향상되고 개발 속도가 빨라집니다. 테스트 구성을 올바르게 설정하면 새로운 개발자를 합류시키는 데 필요한 시간이 단축됩니다. 모든 종속성을 설정할 필요 없이 선택한 구성 파일로 작성된 테스트를 실행하기만 하면 됩니다.