읽기 / 쓰기 기능에 TDD를 어떻게 적용합니까?


10

닭고기와 계란 문제인 것 같습니다.

쓰기 기능을 일부 데이터 저장소에 쓸 수는 있지만 테스트 된 읽기 기능 없이는 올바르게 저장했는지 알 수 없습니다.

데이터 저장소에서 읽기 기능을 읽을 수 있지만 테스트 된 쓰기 기능을 사용하지 않고 해당 데이터 저장소에 데이터를 저장하는 방법은 무엇입니까?

편집하다:

사용하기 위해 객체를 저장하고로드하기 위해 SQL 데이터베이스에 연결하고 트랜잭션을 작성 중입니다. DB가 제공하는 액세스 기능을 테스트하는 것은 중요하지 않지만 객체를 직렬화 / 직렬화 해제하기 위해 이러한 DB 기능을 래핑합니다. DB에 올바른 자료를 올바르게 읽고 쓰고 있는지 확인하고 싶습니다.

@snowman이 언급했듯이 추가 / 삭제와 다릅니다. 내가 작성한 내용이 정확하지만 잘 테스트 된 읽기 기능이 필요하다는 것을 알고 싶습니다. 읽을 때 읽은 내용과 동일한 객체를 올바르게 작성했는지 확인하고 싶습니다. 그러나 잘 테스트 된 쓰기 기능이 필요합니다.


자신의 데이터 저장소를 작성하거나 기존 데이터 저장소를 사용하고 있습니까? 기존 것을 사용하고 있다면 이미 작동한다고 가정하십시오. 직접 작성하는 경우 TDD를 사용하여 다른 소프트웨어를 작성하는 것과 동일한 방식으로 작동합니다.
Robert Harvey


1
밀접하게 관련되어 있지만 이것이 특정 질문과 중복되는 것은 아닙니다. 속임수 대상이 추가 / 삭제에 대해 말하고 있습니다.이 대상은 읽기 / 쓰기입니다. 차이점은 읽기 / 쓰기 종속성은 읽기 / 쓰기 중인 개체 의 내용 에 의존하는 반면 간단한 추가 / 삭제 테스트는 훨씬 간단 할 것 입니다. 개체 가 존재 하는지 여부입니다.

2
읽기 기능을 테스트하기 위해 데이터가 있고 쓰기 기능이없는 데이터베이스를 스테이징 할 수 있습니다.
JeffO

답변:


7

읽기 기능으로 시작하십시오.

  • 테스트 설정에서 : 데이터베이스를 작성하고 테스트 데이터를 추가하십시오. 마이그레이션 스크립트 또는 백업을 통해 이것은 코드가 아니기 때문에 TDD에서 테스트 할 필요가 없습니다.

  • 테스트 : 저장소를 설치하고 테스트 데이터베이스를 가리킨 다음 Read 메소드를 호출하십시오. 테스트 데이터가 리턴되는지 확인하십시오.

이제 완전히 테스트 된 읽기 기능을 가지고 있으며 기존 읽기 기능을 사용하여 자체 결과를 확인할 수있는 쓰기 기능으로 이동할 수 있습니다.


속도를 높이기 위해 메모리에 DB를 만들 수는 있지만 너무 복잡 할 수 있습니다. 단위 테스트에서 모의를 대신 사용하지 않는 이유는 무엇입니까?
BЈовић

1
여기에 약간의 옹호자가 있지만 데이터베이스가 올바르게 작성되었는지 어떻게 테스트합니까? OP가 언급했듯이 닭고기와 달걀.
user949300

1
@ Ewan-나는 당신이 그들의 DB 코드를 테스트해서는 안된다는 것에 전적으로 동의합니다. 하지만 어떻게 당신이 알고 할 당신의 DB 설정 코드가 삽입 곳을 잊지 또는 열에서 잘못된 값을 넣어하지 않았다?
user949300

1
순수한 TDD 접근법에서 테스트는 요구 사항입니다. 논리적으로 잘못 될 수 없습니다. 현실 세계에서 obvs 당신이 그것을 안구
Ewan

1
custodiet ipsos custodes를 종료 하시겠습니까? 또는 "누가 테스트를 테스트합니까?" :-) 나는 순수한 TDD 세계에서 이것이 끔찍하고 지루하고 버그가 발생하기 쉽다는 것에 동의한다. 공감.
user949300

6

나는 종종 쓰기와 읽기를한다. 예 (의사 코드)

Foo foo1 = setup some object to write
File tempfile = create a tempfile, possibly in memory 
writeFoo(foo1, tempfile) 
Foo foo2 = readFoo(tempfile) 
assertEquals(foo1, foo2); 
clean-up goes here

나중에 추가

이 솔루션이 "실용적"이고 "충분히 좋은"것 외에 다른 솔루션 이 잘못된 것을 테스트 한다고 주장 할 수 있습니다. 문자열이나 SQL 문이 일치하는지 테스트하는 것은 끔찍한 아이디어가 아니며, 직접 해봤지만 부작용을 테스트 중이며 깨지기 쉽습니다. 데이터 내에서 대문자를 변경하거나 필드를 추가하거나 버전 번호를 업데이트하면 어떻게됩니까? SQL 드라이버가 효율성을 위해 호출 순서를 전환하거나 업데이트 된 XML 시리얼 라이저가 추가 공간을 추가하거나 스키마 버전을 변경하면 어떻게됩니까?

이제 공식 사양을 엄격히 준수해야한다면 세부 사항을 확인하는 것이 적절하다는 데 동의합니다.


1
90 %가 실제로 밀도가 높은 의사 코드이기 때문에? 확실하지 않다. 텍스트를 강조 표시하고 코드의 노이즈를 줄이십시오.
RubberDuck

1
네, @ 완 열광자는 이것에 눈살을 찌푸 리지만 실용적인 프로그래머는 "충분히 좋다"고 말하고 나아갈 것입니다.
RubberDuck

1
나는 일종의 질문을 읽었습니다. "열심히 마치 TDD를 따르는 것으로 가정합니다 ..."
Ewan

1
OP 및 TDD에 대한 나의 해석은 테스트를 먼저 작성해야하며 다른 곳에서도 테스트하지 않는 한 읽기 및 쓰기를 모두 사용해서는 안된다는 것입니다.
Ewan

2
당신은 '읽는 것은 내가 쓴 것을 반환해야한다'를 테스트하고 있지만 요구 사항은 '읽은 것은 db로부터 데이터를 반환해야한다'와 '쓰기는 데이타를 db에 기록해야한다'
Ewan

4

하지마 I / O를 단위 테스트하지 마십시오. 시간 낭비입니다.

단위 테스트 로직. I / O 코드에서 테스트하려는 로직이 많으면 I / O 수행 방식과 실제 I / O 수행 비즈니스와의 I / O 수행 논리를 분리하기 위해 코드를 리팩터링해야합니다. (테스트하기가 거의 불가능합니다).

조금만 자세히 설명하려면 HTTP 서버를 테스트하려면 통합 테스트와 단위 테스트의 두 가지 테스트 유형을 수행해야합니다. 단위 테스트는 I / O와 전혀 상호 작용하지 않아야합니다. 속도가 느리고 코드의 정확성과 관련이없는 많은 오류 조건이 발생합니다. 단위 테스트는 네트워크 상태에 영향을받지 않아야합니다!

코드는 다음과 같이 구분해야합니다.

  • 전송할 정보를 결정하는 논리
  • 특정 비트의 정보를 전송하기 위해 전송할 바이트를 결정하는 논리 (응답 등을 원시 바이트로 인코딩하는 방법) 및
  • 실제로 해당 바이트를 소켓에 쓰는 메커니즘.

처음 두 가지는 논리와 결정을 포함하며 단위 테스트가 필요합니다. 마지막은 의사 결정을 많이 내리지 않고 통합 테스트를 사용하여 훌륭하게 테스트 할 수 있습니다.

이것은 일반적으로 좋은 디자인이지만 실제로는 테스트하기가 더 쉽기 때문입니다.


여기 예시들이 있습니다 :

  • 관계형 데이터베이스에서 데이터를 가져 오는 코드를 작성하는 경우 관계형 쿼리에서 반환 된 데이터를 응용 프로그램 모델에 매핑하는 방법을 단위 테스트 할 수 있습니다.
  • 데이터를 관계형 데이터베이스에 쓰는 코드를 작성하는 경우 실제로 사용하는 특정 SQL 쿼리를 테스트하지 않고 데이터베이스에 쓰려는 데이터 조각을 단위로 테스트 할 수 있습니다. 예를 들어, 응용 프로그램 상태의 두 복사본을 메모리에 보관할 수 있습니다. 데이터베이스 모양과 작업 복사본을 나타내는 복사본입니다. 데이터베이스와 동기화하려는 경우이를 서로 다른 데이터베이스에 작성해야합니다. diff 코드를 쉽게 단위 테스트 할 수 있습니다.
  • 구성 파일에서 무언가를 읽는 코드를 작성하는 경우 구성 파일 형식 구문 분석기를 테스트하려고하지만 디스크에서 가져온 문자열이 아닌 테스트 소스 파일의 문자열을 사용하십시오.

2

이것이 표준 관행인지 아닌지는 모르겠지만 나에게 잘 작동합니다.

내 비 데이터베이스 읽기 쓰기 방법 구현 에서 구현 세부 사항으로 내 유형별 toString()fromString()방법을 사용합니다.

이것들은 쉽게 테스트 할 수 있습니다 :

 assertEquals("<xml><car type='porsche'>....", new Car("porsche").toString());

실제 읽기 쓰기 방법의 경우 하나의 테스트에서 실제로 읽고 쓰는 하나의 통합 테스트가 있습니다.

그건 그렇고 : 테스트를 함께 읽고 쓰는 하나의 테스트에 문제가 있습니까?


기분이 좋지 않거나 "순수한"느낌이 들지 않지만 이것은 실용적인 해결책입니다.
RubberDuck

나도 테스트를 읽고 쓰는 아이디어가 마음에 든다. toString ()은 실용적으로 타협합니다.
user949300

1

알려진 데이터는 알려진 방식으로 포맷해야합니다. 이것을 구현하는 가장 쉬운 방법은 상수 문자열을 사용하고 @ k3b가 설명한 것처럼 결과를 비교하는 것입니다.

당신은 상수에 제한되지 않습니다. 정규식이나 데이터의 특징을 찾는 애드혹 프로브와 같은 다른 종류의 구문 분석기를 사용하여 추출 할 수있는 기록 된 데이터의 특성이 많이있을 수 있습니다.

데이터를 읽거나 쓰는 경우, 시스템의 다른 부분과의 간섭없이 테스트를 실행할 수있는 메모리 내 파일 시스템을 갖는 것이 유용 할 수 있습니다. 인 메모리 파일 시스템에 액세스 할 수 없으면 임시 디렉토리 트리를 사용하십시오.


1

의존성 주입과 조롱을 사용하십시오.

SQL 드라이버를 테스트하지 않고 SQL 데이터베이스가 온라인 상태이고 올바르게 설정되어 있는지 테스트하고 싶지 않습니다. 이는 통합 또는 시스템 테스트의 일부입니다. 코드가 전송해야하는 SQL 문을 전송하는지와 응답을 예상대로 해석하는지 테스트하려고합니다.

따라서 데이터베이스로 무언가를 수행 해야하는 메소드 / 클래스가있을 때 자체 데이터베이스 연결을 얻지 마십시오. 데이터베이스 연결을 나타내는 오브젝트가 전달되도록 변경하십시오.

프로덕션 코드에서 실제 데이터베이스 오브젝트를 전달하십시오.

단위 테스트에서 실제 데이터베이스가 실제로 데이터베이스 서버에 연결되지 않는 것처럼 동작하는 모의 객체를 전달하십시오. 수신해야하는 SQL 문을 수신 한 후 하드 코딩 된 응답으로 응답하는지 확인하십시오.

이런 식으로 실제 데이터베이스가 없어도 데이터베이스 추상화 계층을 테스트 할 수 있습니다.


Devil 's Advocate : "수신해야 할"SQL 문을 어떻게 알 수 있습니까? DB 드라이버가 코드에 표시된 순서를 최적화하면 어떻게 되나요?
user949300

@ user949300 데이터베이스 모의 객체는 일반적으로 데이터베이스 드라이버를 대체 합니다.
Philipp

저장소를 테스트 할 때 모의 데이터베이스 클라이언트를 주입 할 필요가 없습니다. 코드가 데이터베이스에서 작동하는 sql을 실행하는지 테스트해야합니다. 그렇지 않으면 당신은 단지 당신의 모의를 테스트 할 것입니다
Ewan

@Ewan 그것은 단위 테스트에 관한 것이 아닙니다. 단위 테스트는 다른 세계와 분리 된 하나의 코드 단위를 테스트합니다. 코드 및 데이터베이스와 같은 구성 요소 간의 상호 작용을 테스트하지 않습니다. 이것이 바로 통합 테스트입니다.
Philipp

예. 나는 DB 저장소를 테스트하는 포인트 단위가 없다고 말합니다. 통합 테스트는 유일하게 할 가치가 있습니다
Ewan

0

객체 관계형 매퍼를 사용하는 경우 일반적으로 집계를 생성, 유지 및 새 세션에서 다시로드 한 후 상태를 확인하여 매핑이 올바르게 작동하는지 테스트하는 데 사용할 수있는 관련 라이브러리가 있습니다. 원래의 객체

NHibernate는 지속성 사양 테스트를 제공합니다 . 빠른 단위 테스트를 위해 메모리 내 저장소에 대해 작동하도록 구성 할 수 있습니다.

가장 간단한 버전의 리포지토리 및 작업 단위 패턴을 따르고 모든 매핑을 테스트하는 경우 거의 모든 작업을 수행 할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.