단위 테스트 데이터베이스 중심 애플리케이션을위한 최상의 전략은 무엇입니까?

StackOverflow https://stackoverflow.com/questions/145131

문제

백엔드에서 다양한 복잡성 데이터베이스에 의해 구동되는 많은 웹 애플리케이션에서 작업합니다. 일반적으로 an이 있습니다 ORM 비즈니스 및 프리젠 테이션 논리와 별도로 레이어. 이것은 비즈니스 논리를 상당히 간단하게 만듭니다. 사물은 불연속 모듈에서 구현할 수 있으며 테스트에 필요한 모든 데이터는 객체 조롱을 통해 가짜를 만들 수 있습니다.

그러나 ORM과 데이터베이스 자체를 테스트하는 것은 항상 문제와 타협으로 가득 차있었습니다.

수년에 걸쳐, 나는 몇 가지 전략을 시도했지만 그 중 어느 것도 나를 완전히 만족시키지 못했습니다.

  • 알려진 데이터로 테스트 데이터베이스를로드하십시오. ORM에 대한 테스트를 실행하고 올바른 데이터가 다시 오는지 확인하십시오. 여기서 단점은 테스트 DB가 응용 프로그램 데이터베이스의 스키마 변경 사항을 따라야하며 동기화가 나올 수 있다는 것입니다. 또한 인공 데이터에 의존하며 어리석은 사용자 입력으로 인해 발생하는 버그를 노출하지 않을 수 있습니다. 마지막으로 테스트 데이터베이스가 작 으면 누락 된 인덱스와 같은 비 효율성이 나타나지 않습니다. (좋아요, 마지막 것은 실제로 단위 테스트를 사용해야하는 것이 아니라 아프지 않습니다.)

  • 프로덕션 데이터베이스의 사본을로드하고 이에 대해 테스트하십시오. 여기서 문제는 주어진 시간에 생산 DB에 무엇이 있는지 전혀 모른다는 것입니다. 시간이 지남에 따라 데이터가 변경되면 테스트를 다시 작성해야 할 수 있습니다.

어떤 사람들은이 두 가지 전략이 특정 데이터에 의존하고 단위 테스트는 기능 만 테스트해야한다고 지적했습니다. 이를 위해 나는 제안 된 것을 보았다.

  • 모의 데이터베이스 서버를 사용하고 ORM이 주어진 메소드 호출에 대한 응답으로 올바른 쿼리를 보내고 있는지 확인하십시오.

데이터베이스 구동 애플리케이션을 테스트하는 데 어떤 전략을 사용 했습니까? 당신을 위해 최선을 다한 것은 무엇입니까?

도움이 되었습니까?

해결책

나는 실제로 당신의 첫 번째 접근 방식을 상당히 성공적으로 사용했지만 약간 다른 방식으로 몇 가지 문제를 해결할 것이라고 생각합니다.

  1. 체크 아웃 후 누구나 현재 데이터베이스 스키마를 만들 수 있도록 소스 컨트롤에서 만들기 위해 전체 스키마와 스크립트를 유지하십시오. 또한 빌드 프로세스의 일부로로드되는 데이터 파일에 샘플 데이터를 유지하십시오. 오류를 일으키는 데이터를 발견하면 샘플 데이터에 추가하여 오류가 다시 나타나지 않는지 확인하십시오.

  2. 연속 통합 서버를 사용하여 데이터베이스 스키마를 구축하고 샘플 데이터를로드하고 테스트를 실행하십시오. 이것이 테스트 데이터베이스를 동기화로 유지하는 방법입니다 (모든 테스트 실행시 재 구축). 이를 위해서는 CI 서버가 자체 전용 데이터베이스 인스턴스의 액세스 및 소유권이 있어야하지만 하루에 3 번 DB 스키마를 구축하면 배송 직전까지 발견되지 않은 오류를 찾는 데 도움이되었다고 말합니다 (나중에는 그렇지 않은 경우. ). 나는 매번 커밋하기 전에 스키마를 재건한다고 말할 수 없습니다. 아무도 없나요? 이 접근법을 통해 당신은 그럴 필요가 없습니다 (아마도 우리는해야하지만 누군가가 잊어 버린 경우 큰 문제는 아닙니다).

  3. 내 그룹의 경우 사용자 입력은 응용 프로그램 수준 (DB 아님)에서 수행되므로 표준 단위 테스트를 통해 테스트됩니다.

프로덕션 데이터베이스 사본로드 :
이것은 나의 마지막 직업에서 사용 된 접근법이었습니다. 몇 가지 문제의 큰 통증 원인이었습니다.

  1. 사본은 프로덕션 버전에서 구축됩니다.
  2. 사본 스키마가 변경되며 생산 시스템으로 전파되지 않습니다. 이 시점에서 우리는 스키마를 분기 시켰습니다. 재미 없어.

데이터베이스 서버 조롱 :
우리는 또한 내 직장에서 이것을합니다. 모든 커밋 후 우리는 모의 DB 액세서가 주입 한 응용 프로그램 코드에 대해 단위 테스트를 실행합니다. 그런 다음 하루에 세 번 위에서 설명한 전체 DB 빌드를 실행합니다. 나는 확실히 두 가지 접근법을 추천합니다.

다른 팁

나는 항상 이런 이유로 메모리 내 DB (HSQLDB 또는 Derby)에 대한 테스트를 실행하고 있습니다.

  • 테스트 DB에 보관할 데이터와 그 이유를 생각하게 만듭니다. 당신의 생산 DB를 테스트 시스템으로 끌어들이는 것만으로 "내가 무엇을하고 있는지, 왜 그리고 무언가가 깨지는 지 모르겠다. ;)
  • 새로운 장소에서 거의 노력하지 않고 데이터베이스를 재현 할 수 있도록합니다 (예 : 생산에서 버그를 복제해야 할 때)
  • DDL 파일의 품질에 큰 도움이됩니다.

메모리 내 DB에는 테스트가 시작되면 신선한 데이터가로드되고 대부분의 테스트 후 롤백을 호출하여 안정적으로 유지합니다. 언제나 테스트 DB의 데이터를 안정적으로 유지하십시오! 데이터가 항상 변경되면 테스트 할 수 없습니다.

데이터는 SQL, 템플릿 DB 또는 덤프/백업에서로드됩니다. 덤프가 VC에 넣을 수 있기 때문에 읽을 수있는 형식 인 경우 덤프를 선호합니다. 그래도 작동하지 않으면 CSV 파일 또는 XML을 사용합니다. 막대한 양의 데이터를로드해야한다면 ... 나는 그렇지 않습니다. 단위 테스트에 대한 막대한 양의 데이터를로드 할 필요가 없습니다. 성능 테스트는 또 다른 문제이며 다른 규칙이 적용됩니다.

나는 오랫동안이 질문을 해왔지만, 그에 대한은 총알은 없다고 생각합니다.

내가 현재하고있는 것은 DAO 객체를 조롱하고 데이터베이스에 살 수있는 흥미로운 데이터 사례를 나타내는 좋은 객체 모음을 메모리 표현하는 것입니다.

내가 그 접근법에서 볼 수있는 주요 문제는 DAO 계층과 상호 작용하는 코드 만 다루고 있지만 DAO 자체를 테스트하지는 않는다는 것입니다. 내 경험상 그 계층에서도 많은 오류가 발생한다는 것을 알 수 있습니다. 또한 데이터베이스에 대해 실행되는 몇 가지 단위 테스트를 유지하지만 (TDD를 사용하거나 로컬로 빠르게 테스트하기 위해) 해당 테스트는 해당 목적으로 데이터베이스를 보관하지 않기 때문에 연속 통합 서버에서 실행되지 않습니다. CI 서버에서 실행되는 테스트는 자체 포함되어야한다고 생각하십시오.

내가 매우 흥미롭지 만 항상 가치가있는 것은 아니지만 항상 시간이 많이 걸리는 것은 항상 가치가 없으며, 단위 테스트 내에서 실행되는 임베디드 데이터베이스에서 생산에 사용하는 것과 동일한 스키마를 만드는 것입니다.

의문의 여지가 없지만이 접근법이 적용 범위를 향상시킬 수는 없지만 현재 DBM 및 내장 교체와 함께 작동하도록 ANSI SQL에 최대한 가깝기 때문에 몇 가지 단점이 있습니다.

코드와 더 관련성이 있다고 생각하든 몇 가지 프로젝트가 있습니다. DBUNIT.

데이터베이스를 조롱 할 수있는 도구가 있더라도 (예 : 주크'에스 MockConnection, 볼 수 있습니다 이 답변 - 면책 조항, 나는 jooq의 공급 업체를 위해 일합니다), 나는 조언 할 것입니다. ~ 아니다 복잡한 쿼리로 더 큰 데이터베이스를 조롱합니다.

ORM을 통합하고 싶더라도 ORM은 데이터베이스에 매우 복잡한 일련의 쿼리를 발행하여 다를 수 있습니다.

  • 통사론
  • 복잡성
  • 주문하다 (!)

현명한 더미 데이터를 생성하기 위해 모든 것을 조롱하는 것은 실제로 모의 내부에 작은 데이터베이스를 구축하지 않는 한 매우 어렵습니다. 그렇게 말하면 잘 알려진 통합 테스트 데이터베이스를 사용하여 잘 알려진 데이터로 쉽게 재설정 할 수 있으며 통합 테스트를 실행할 수 있습니다.

첫 번째 (테스트 데이터베이스에 대해 코드 실행)를 사용합니다. 이 접근 방식으로 제기하는 유일한 실질적인 문제는 Schemas가 동기화를 벗어나는 가능성이 있습니다. 데이터베이스의 버전 번호를 유지하고 각 버전 증분에 대한 변경 사항을 적용하는 스크립트를 통해 모든 스키마를 변경하여 처리합니다.

또한 테스트 환경에 대해 먼저 데이터베이스 스키마를 포함하여 모든 변경 (데이터베이스 스키마 포함)을 먼저 수행하므로 다른 방법으로 끝납니다. 모든 테스트가 통과 한 후 스키마 업데이트를 프로덕션 호스트에 적용합니다. 또한 개발 시스템에 별도의 테스트 대 애플리케이션 데이터베이스를 보관하여 실제 생산 상자 (ES)를 터치하기 전에 DB 업그레이드가 올바르게 작동하는지 확인할 수 있습니다.

첫 번째 접근 방식을 사용하고 있지만 언급 한 문제를 해결할 수있는 약간 다릅니다.

DAO에 대한 테스트를 실행하는 데 필요한 모든 것은 소스 제어에 있습니다. 스키마와 스크립트가 포함되어 DB를 생성합니다 (Docker는 이에 매우 좋습니다). 임베디드 DB를 사용할 수있는 경우 속도로 사용합니다.

다른 설명 된 접근법과의 중요한 차이점은 테스트에 필요한 데이터가 SQL 스크립트 또는 XML 파일에서로드되지 않았다는 것입니다. 유틸리티 함수/클래스를 사용하여 응용 프로그램에 의해 효과적으로 일정하게 일정한 일부 (일부 사전 데이터 제외)는 모든 것입니다.

주요 목적은 테스트에서 데이터를 사용하는 것입니다.

  1. 테스트에 매우 가깝습니다
  2. 명시 적 (데이터에 SQL 파일을 사용하면 어떤 테스트에서 어떤 데이터가 사용되는지 확인하는 것이 매우 문제가됩니다)
  3. 관련없는 변경으로부터 테스트를 분리하십시오.

기본적으로 이러한 유틸리티는 테스트 자체에서 테스트에 필수적인 것만 선언하고 관련없는 것들을 생략 할 수 있음을 의미합니다.

실제로 그것이 무엇을 의미하는지에 대한 아이디어를 주려면 함께 작동하는 일부 DAO에 대한 테스트를 고려하십시오. Comments to PostS가 작성했습니다 Authors. 이러한 DAO에 대한 CRUD 작업을 테스트하려면 DB에서 일부 데이터가 작성되어야합니다. 테스트는 다음과 같습니다.

@Test
public void savedCommentCanBeRead() {
    // Builder is needed to declaratively specify the entity with all attributes relevant
    // for this specific test
    // Missing attributes are generated with reasonable values
    // factory's responsibility is to create entity (and all entities required by it
    //  in our example Author) in the DB
    Post post = factory.create(PostBuilder.post());

    Comment comment = CommentBuilder.comment().forPost(post).build();

    sut.save(comment);

    Comment savedComment = sut.get(comment.getId());

    // this checks fields that are directly stored
    assertThat(saveComment, fieldwiseEqualTo(comment));
    // if there are some fields that are generated during save check them separately
    assertThat(saveComment.getGeneratedField(), equalTo(expectedValue));        
}

테스트 데이터가있는 SQL 스크립트 또는 XML 파일에 비해 몇 가지 장점이 있습니다.

  1. 코드를 유지하는 것이 훨씬 쉽습니다 (저자와 같은 많은 테스트에서 참조되는 일부 엔티티에서 필수 열을 추가하면 많은 파일/레코드를 변경할 필요는 없지만 빌더 및/또는 공장의 변경 만 필요합니다).
  2. 특정 테스트에 필요한 데이터는 다른 파일이 아닌 테스트 자체에 설명되어 있습니다. 이 근접성은 테스트 이해력에 매우 중요합니다.

롤백 대 커밋

시험이 실행될 때 테스트가 저지른 것이 더 편리하다는 것을 알게되었습니다. 첫째, 일부 효과 (예를 들어 DEFERRED CONSTRAINTS) 커밋이 발생하지 않으면 확인할 수 없습니다. 둘째, 테스트가 실패하면 롤백으로 되돌아 가지 않기 때문에 데이터를 DB에서 검사 할 수 있습니다.

원인은 테스트가 데이터가 깨진 데이터를 생성 할 수 있다는 단점이 있으며 이는 다른 테스트에서 실패로 이어질 것입니다. 이것을 다루기 위해 나는 테스트를 분리하려고 노력합니다. 위의 예에서 모든 테스트는 새로 생성 될 수 있습니다 Author 그리고 다른 모든 엔티티는 그것과 관련하여 만들어 지므로 충돌은 드 rare니다. 잠재적으로 파손될 수는 있지만 DB 레벨 제약 조건으로 표현할 수없는 나머지 불변량을 처리하려면 단일 테스트 후 실행될 수있는 잘못된 조건에 대해 프로그래밍 방식 검사를 사용합니다 (그리고 CI에서 실행되지만 일반적으로 성능을 위해 로컬로 꺼져 있습니다. 원인).

JDBC 기반 프로젝트 (직접 또는 간접적으로 JPA, EJB, ...)의 경우 전체 데이터베이스가 아닌 (실제 RDBM에서 테스트 DB를 사용하는 것이 좋습니다) JDBC 레벨에서만 모킹 할 수 있습니다. .

JDBC 데이터 (결과 세트, 업데이트 카운트, 경고, ...)가 백엔드와 동일하기 때문에 장점은 이러한 방식으로 제공되는 추상화입니다. Prod DB, 테스트 DB 또는 각 테스트에 제공되는 일부 모형 데이터 사례.

각 케이스마다 JDBC 연결이 조롱되면 테스트 DB를 관리 할 필요가 없습니다 (정리, 시간에 단 하나의 테스트, 재 장전 고정 장치 등). 모든 모형 연결이 분리되어 정리할 필요가 없습니다. 각 테스트 사례에는 JDBC 교환을 조롱하기 위해 최소한의 필수 비품 만 제공되므로 전체 테스트 DB 관리의 복잡성을 피하는 데 도움이됩니다.

Acolyte는 JDBC 드라이버와 이러한 종류의 모형을위한 유틸리티를 포함하는 프레임 워크입니다. http://acolyte.eu.org .

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top