단위 테스트에서 주기적 종속성으로 어려움을 겪고 있습니다.


24

비트 벡터와 같은 간단한 것을 개발하기 위해 TDD를 사용하려고합니다. Swift를 사용하고 있지만 언어에 구애받지 않는 질문입니다.

My BitVectorstructsingle을 저장 UInt64하고 컬렉션처럼 취급 할 수있는 API를 제공합니다. 세부 사항은별로 중요하지 않지만 매우 간단합니다. 상위 57 비트는 스토리지 비트이고 하위 6 비트는 "카운트"비트이며 실제로 얼마나 많은 스토리지 비트가 포함 된 값을 저장하는지 알려줍니다.

지금까지 몇 가지 매우 간단한 기능이 있습니다.

  1. 빈 비트 벡터를 구성하는 이니셜 라이저
  2. count유형 의 속성Int
  3. isEmpty유형 의 속성Bool
  4. 항등 연산자 ( ==) 주의 : 이것은 Object.equals()자바 와 비슷한 가치 평등 연산자 이며, ==자바 와 같은 참조 평등 연산자가 아닙니다 .

나는 주기적으로 많은 의존성을 겪고 있습니다.

  1. 이니셜 라이저를 테스트하는 단위 테스트는 새로 생성되었는지 확인해야합니다 BitVector. 다음 세 가지 방법 중 하나로 수행 할 수 있습니다.

    1. 검사 bv.count == 0
    2. 검사 bv.isEmpty == true
    3. 확인 bv == knownEmptyBitVector

    방법 1에 의존하고 count, 방법 2에 의존하고 isEmpty(자체에 의존 count하므로 사용하지 않아도 됨) 방법 3에 의존합니다 ==. 어쨌든 초기화 프로그램을 독립적으로 테스트 할 수 없습니다.

  2. 에 대한 테스트 count는 필연적으로 내 초기화 프로그램을 테스트하는 무언가에서 작동해야합니다.

  3. 의 구현에 isEmpty의존count

  4. 의 구현은에 ==의존합니다 count.

BitVector기존 비트 패턴 ( UInt64) 을 구성하는 개인 API를 도입하여이 문제를 부분적으로 해결할 수있었습니다 . 이를 통해 다른 이니셜 라이저를 테스트하지 않고 값을 초기화 할 수 있었으므로 "부트 스트랩"할 수있었습니다.

단위 테스트가 실제로 단위 테스트가 되려면 많은 해킹을 수행하여 제품 코드와 테스트 코드를 상당히 복잡하게 만듭니다.

이런 종류의 문제를 정확히 어떻게 해결합니까?


20
"단위"라는 용어를 너무 좁게보고 있습니다. BitVector는 단위 테스트를위한 완벽한 단위 크기이며 공개 구성원이 BitVector서로 의미있는 테스트를 수행해야하는 문제를 즉시 해결합니다 .
바트 반 Ingen Schenau

구현 세부 사항이 너무 많습니다. 개발이 실제로 테스트 중심 입니까?
herby

@ 허비 아뇨, 제가 연습하는 이유입니다. 그것은 실제로 달성 할 수없는 표준처럼 보입니다. 구현에 수반되는 것에 대한 명확한 정신적 근사없이 아무것도 프로그래밍하지 않았습니다.
Alexander-복원 모니카

@Alexander 당신은 그것을 휴식하려고 노력해야합니다. 그렇지 않으면 테스트 우선이지만 테스트 중심은 아닙니다. 막연하게 "백업 저장소로 하나의 64 비트 int로 비트 벡터를 할 것"이라고 말하면됩니다. 그 시점부터 TDD 적 녹색 리팩터링을 차례로 수행하십시오. API뿐만 아니라 구현 세부 사항도 테스트를 실행하려고 시도하고 (이전) 테스트를 처음부터 작성해야합니다 (후자).
herby

답변:


66

구현 세부 사항에 대해 너무 걱정하고 있습니다.

현재 구현 에서는 isEmpty의존 count하거나 다른 관계 에 의존 한다는 것은 중요하지 않습니다 . 관심을 가져야 할 것은 공용 인터페이스뿐입니다. 예를 들어 세 가지 테스트를 수행 할 수 있습니다.

  • 새로 초기화 된 객체는 것을 count == 0.
  • 새로 초기화 된 객체는 isEmpty == true
  • 새로 초기화 된 객체는 알려진 빈 객체와 같습니다.

이것들은 모두 유효한 테스트이며, 클래스의 내부를 리팩토링하기로 결정하여 isEmpty의존하지 않는 다른 구현을 하기로 결정한 경우 특히 중요 count합니다. 테스트가 모두 통과하는 한 회귀하지 않았다는 것을 알고 있습니다. 아무것도.

비슷한 점이 다른 점에도 적용됩니다. 내부 구현이 아니라 공용 인터페이스를 테스트해야합니다. 여기에 TDD가 유용하다는 것을 알 수 있습니다. TDD를 isEmpty구현하기 전에 필요한 테스트를 작성하기 때문입니다.


6
@Alexander 단위 테스트에 대한 명확한 정의가 필요한 사람처럼 들립니다. 내가 아는 가장 좋은 것은 Michael Feathers
candied_orange

14
@Alexander 당신은 각 방법을 독립적으로 테스트 가능한 코드 조각으로 취급합니다. 그것이 어려움의 근원입니다. 객체를 더 작은 부분으로 나누지 않고 전체적으로 테스트하면 이러한 어려움이 사라집니다. 객체 간의 종속성은 메서드 간의 종속성과 비교할 수 없습니다.
amon

9
@Alexander "코드 조각"은 임의 측정입니다. 변수를 초기화하기 만하면 많은 "조각"을 사용하게됩니다. 중요한 것은 귀하가 정의한대로 응집력있는 행동 단위 를 테스트하고 있다는 입니다.
개미 P

9
"내가 읽은 바에 따르면 코드 조각 만 깨면 해당 코드와 직접 관련된 단위 테스트 만 실패한다는 인상을 받았습니다." 그것은 따르는 것이 매우 어려운 규칙 인 것 같습니다 . (예를 들어, 벡터 클래스를 작성하고 index 메소드에서 오류가 발생하면 해당 벡터 클래스를 사용하는 모든 코드에서 많은 양의 손상이 발생했을 것입니다)
jhominal

4
@Alexander 또한 테스트를 위해 "배열, 행위, 어설 션"패턴을 살펴보십시오. 기본적으로 객체는 (정렬) 상태에 있어야하고 실제로 테스트중인 메소드를 호출 (Act) 한 다음 예상에 따라 상태가 변경되었는지 확인합니다. (어설 션). 배열에서 설정 한 것은 테스트를위한 "전제 조건"입니다.
GalacticCowboy

5

이런 종류의 문제를 정확히 어떻게 해결합니까?

당신은 "단위 테스트"가 무엇인지에 대한 생각을 수정합니다.

메모리에서 변경 가능한 데이터를 관리하는 객체는 기본적으로 상태 머신입니다. 어떤 가치있는 사용 사례가 정보를 넣어하는 방법 호출 최소한으로 가고 그래서 으로 개체에 대한 정보의 사본을 읽을 수있는 대상 및 방법 호출합니다. 흥미로운 사용 사례에서는 데이터 구조를 변경하는 추가 메소드를 호출 할 것입니다.

실제로 이것은 종종 다음과 같습니다

// GIVEN
obj = new Object(...)

// THEN
assert object.read(...)

또는

// GIVEN
obj = new Object(...)

// WHEN
object.change(...)

// THEN
assert object.read(...)

"단위 테스트"용어-글쎄요, 그것은 아주 좋지 않은 역사를 가지고 있습니다.

단위 테스트라고 부르지 만 단위 테스트의 허용 된 정의와 잘 맞지 않습니다-Kent Beck, 예제 별 테스트 주도 개발

켄트는 1994 년 SUnit의 첫 번째 버전을 썼으며 JUnit의 포트는 1998 년에 TDD 서적의 첫 번째 초안은 2002 년 초에 작성되었습니다. 혼란이 확산되기까지 많은 시간이 걸렸습니다.

이러한 테스트의 핵심 아이디어 (보다 정확하게는 "프로그래머 테스트"또는 "개발자 테스트"라고 함)는 테스트 가 서로 분리되어 있다는 것 입니다. 테스트는 변경 가능한 데이터 구조를 공유하지 않으므로 동시에 실행할 수 있습니다. 솔루션을 올바르게 측정하기 위해 테스트를 특정 순서로 실행해야한다는 걱정은 없습니다.

이러한 테스트의 주요 사용 사례는 프로그래머가 자신의 소스 코드를 편집 할 때마다 실행한다는 것입니다. 빨간색 녹색 리 팩터 프로토콜을 수행하는 경우 예기치 않은 빨간색은 항상 마지막 편집의 결함을 나타냅니다. 변경 사항을 되돌리고 테스트가 녹색인지 확인한 후 다시 시도하십시오. 가능한 모든 버그가 하나의 테스트에 의해 잡히는 디자인에 투자하려고 시도 할 때 많은 이점이 없습니다.

물론, 병합으로 인해 오류가 발생하면 오류가 더 이상 사소한 것이 아닙니다. 결함을 쉽게 지역화하기 위해 수행 할 수있는 다양한 단계가 있습니다. 만나다


1

일반적으로 (TDD를 사용하지 않더라도) 구현 방법을 모르는 척하면서 테스트를 최대한 많이 작성해야합니다.

실제로 TDD를하고 있다면 이미 그럴 것입니다. 테스트는 프로그램의 실행 사양입니다.

테스트 자체가 합리적이고 잘 유지되는 한 호출 그래프가 테스트 아래에 표시되는 방식은 관련이 없습니다.

문제가 TDD에 대한 이해라고 생각합니다.

내 생각에 당신의 문제는 당신이 당신의 TDD 페르소나를 "혼합"하고 있다는 것입니다. "테스트", "코드"및 "리 팩터"개인은 서로 완전히 독립적으로 이상적으로 작동합니다. 특히 코딩 및 리팩토링 담당자는 테스트를 녹색으로 유지 / 유지하는 것 외에는 테스트 할 의무가 없습니다.

원칙적으로 모든 테스트가 직교적이고 독립적 인 것이 가장 좋습니다. 그러나 이것은 다른 두 TDD 페르소나의 문제가 아니며 테스트의 엄격하거나 현실적인 현실적 요구 사항은 아닙니다. 기본적으로 : 아무도 요구하지 않는 요구 사항을 충족시키기 위해 코드 품질에 대한 상식적인 감정을 버리지 마십시오.

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