모든 것을 공개하고 조롱하는 단위 테스트 포인트가 있습니까?


58

단위 테스트를 "적절한"방법으로 수행 할 때, 즉 모든 공중 전화를 스터 빙하고 사전 설정 값 또는 모의를 반환하면 실제로 아무것도 테스트하지 않는 것 같습니다. 말 그대로 내 코드를보고 공개 메소드를 통한 논리 흐름을 기반으로 예제를 작성하고 있습니다. 그리고 구현이 바뀔 때마다 나는 그 테스트를 다시 바꿔야합니다. 실제로 유용한 것을 성취하고 있다고 느끼지 않아야합니다 (중장기이든 장기적인 것인지). 또한 통합 테스트 (행복하지 않은 경로 포함)를 수행하고 테스트 시간이 길어지는 것을 정말로 신경 쓰지 않습니다. 그것들로, 나는 실제로 회귀를 테스트하고 있다고 느낍니다. 왜냐하면 그것들은 여러 번 잡 혔기 때문에 단위 테스트가 수행하는 모든 것은 이미 알고있는 공개 메소드의 구현이 변경되었음을 보여줍니다.

단위 테스트는 방대한 주제이며, 나는 여기서 무언가를 이해하지 못하는 것 같습니다. 단위 테스트와 통합 테스트 (시간 오버 헤드 제외)의 결정적인 이점은 무엇입니까?


4
내 두 센트 : 모의 물을 과도하게 사용하지 마십시오 ( googletesting.blogspot.com/2013/05/…
Juan Mendes

답변:


37

단위 테스트를 "적절한"방법으로 수행 할 때, 즉 모든 공중 전화를 스터 빙하고 사전 설정 값 또는 모의를 반환하면 실제로 아무것도 테스트하지 않는 것 같습니다. 말 그대로 내 코드를보고 공개 메소드를 통한 논리 흐름을 기반으로 예제를 작성하고 있습니다.

이것은 테스트하는 메소드가 다른 몇 가지 클래스 인스턴스 (모의해야 함)가 필요하고 여러 메소드를 자체적으로 호출하는 것처럼 들립니다.

이러한 유형의 코드는 개요로 인해 실제로 단위 테스트가 어렵습니다.

내가 찾은 것은 그러한 클래스를 다음과 같이 나누는 것입니다.

  1. 실제 "비즈니스 로직"이있는 클래스. 이것들은 다른 클래스에 대한 호출을 거의 또는 전혀 사용하지 않으며 테스트하기 쉽습니다 (값 인-아웃).
  2. 외부 시스템 (파일, 데이터베이스 등)과 인터페이스하는 클래스 이것들은 외부 시스템을 감싸고 당신의 필요에 편리한 인터페이스를 제공합니다.
  3. "모든 것을 하나로 묶는"클래스

그런 다음 1의 클래스는 값을 수락하고 결과를 반환하기 때문에 단위 테스트가 쉽습니다. 더 복잡한 경우,이 클래스는 자체적으로 호출을 수행해야 할 수도 있지만 2에서 클래스 만 호출하고 (예 : 데이터베이스 함수를 직접 호출하지 않음) 2의 클래스는 조롱하기 쉽습니다 (만 해당되기 때문에) 랩핑 된 시스템의 일부를 노출 시키십시오).

2와 3의 클래스는 일반적으로 의미있는 단위 테스트를 수행 할 수 없습니다. OTOH는 이러한 클래스가 비교적 단순하고 소수 인 경향이 있으므로 통합 테스트를 적절히 다루어야합니다.


한 클래스

데이터베이스에서 가격을 검색하고 할인을 적용한 다음 데이터베이스를 업데이트하는 클래스가 있다고 가정하십시오.

이 클래스를 모두 한 클래스에 보유하고 있다면 조롱하기 어려운 DB 함수를 호출해야합니다. 의사 코드에서 :

1 select price from database
2 perform price calculation, possibly fetching parameters from database
3 update price in database

세 단계 모두 DB 액세스가 필요하므로 많은 (복잡한) 조롱이 필요하므로 코드 또는 DB 구조가 변경되면 깨질 수 있습니다.

헤어지다

PriceCalculation, PriceRepository, App의 세 가지 클래스로 나뉩니다.

PriceCalculation은 실제 계산 만 수행하고 필요한 값을 제공합니다. 앱은 모든 것을 하나로 묶습니다.

App:
fetch price data from PriceRepository
call PriceCalculation with input values
call PriceRepository to update prices

그런 식으로:

  • PriceCalculation은 "비즈니스 로직"을 캡슐화합니다. 자체적으로 아무것도 호출하지 않기 때문에 테스트하기 쉽습니다.
  • 모의 데이터베이스를 설정하고 읽기 및 업데이트 호출을 테스트하여 PriceRepository를 의사 단위 테스트 할 수 있습니다. 논리가 거의 없으므로 코드 경로가 거의 없으므로 이러한 테스트를 너무 많이 필요로하지 않습니다.
  • 앱은 접착제 코드이기 때문에 의미있게 단위 테스트를 할 수 없습니다. 그러나 그것도 매우 간단하므로 통합 테스트로 충분합니다. 나중에 앱이 너무 복잡해지면 더 많은 "비즈니스 논리"클래스를 분류합니다.

마지막으로 PriceCalculation은 자체 데이터베이스 호출을 수행해야합니다. 예를 들어 PriceCalculation만이 필요한 데이터를 알고 있기 때문에 App에서 미리 가져올 수 없습니다. 그런 다음 PriceRepository 인스턴스 (또는 다른 리포지토리 클래스)를 PriceCalculation의 요구에 맞게 사용자 지정할 수 있습니다. 이 클래스는 조롱해야하지만 PriceRepository의 인터페이스는 단순하기 때문에 간단합니다 (예 :) PriceRepository.getPrice(articleNo, contractType). 가장 중요한 점은 PriceRepository의 인터페이스가 데이터베이스에서 PriceCalculation을 분리하므로 DB 스키마 또는 데이터 조직을 변경해도 인터페이스가 변경되지 않아 모의가 손상 될 가능성이 없다는 것입니다.


5
나는 덕분에, 나는 모든 것을 테스트 단위로 점을보고 혼자라고 생각
enthrops

4
나는 타입 3의 클래스가 거의 없다고 말할 때 단지 동의하지 않습니다. 내 코드의 대부분이 타입 3이고 비즈니스 로직이 거의 없다고 생각합니다. 이것이 내가 의미하는 바 : stackoverflow.com/questions/38496185/…
Rodrigo Ruiz

27

단위 테스트와 통합 테스트의 결정적인 장점은 무엇입니까?

그것은 틀린 이분법입니다.

단위 테스트 및 통합 테스트는 유사하지만 서로 다른 두 가지 용도로 사용됩니다. 단위 테스트의 목적은 분석법이 작동하는지 확인하는 것입니다. 실제 용어로, 단위 테스트는 코드 가 단위 테스트에 의해 요약 된 계약을 충족 하는지 확인합니다 . 이것은 단위 테스트가 설계되는 방식에서 분명합니다. 코드는 무엇을해야하는지 명시하고 코드가 그렇게 한다고 주장 합니다.

통합 테스트는 다릅니다. 통합 테스트는 소프트웨어 구성 요소 간의 상호 작용을 연습합니다. 모든 테스트를 통과하고 올바르게 상호 작용하지 않기 때문에 통합 테스트에 실패하는 소프트웨어 구성 요소를 가질 수 있습니다.

그러나 단위 테스트에 결정적인 이점이 있다면 다음과 같습니다. 단위 테스트를 설정하는 것이 훨씬 쉽고 통합 테스트보다 훨씬 적은 시간과 노력이 필요합니다. 올바르게 사용하면 단위 테스트를 통해 "테스트 가능한"코드를 개발할 수 있습니다. 즉, 최종 결과가보다 안정적이고 이해하기 쉬우 며 유지 관리하기 쉬워집니다. 테스트 가능한 코드는 일관된 API, 반복 가능한 동작과 같은 특정 특성을 가지고 있으며 주장하기 쉬운 결과를 반환합니다.

복잡한 테스트, 복잡한 설정 및 어려운 어설 션이 필요한 경우가 많으므로 통합 테스트는 더 어렵고 비용이 많이 듭니다. 최고 수준의 시스템 통합에서 UI에서 사람의 상호 작용을 시뮬레이션하려고합니다. 전체 소프트웨어 시스템은 이러한 종류의 자동화에 전념합니다. 그리고 우리가 추구하는 것은 자동화입니다. 휴먼 테스트는 반복 할 수 없으며 자동화 된 테스트처럼 확장되지 않습니다.

마지막으로 통합 테스트는 코드 범위를 보장하지 않습니다. 통합 테스트로 몇 개의 코드 루프, 조건 및 분기 조합을 테스트하고 있습니까? 당신은 정말로 알고 있습니까? 단위 테스트 및 테스트중인 메소드와 함께 사용할 수있는 도구가 있으며 코드 범위 및 코드의 순환 복잡도를 알려줍니다. 그러나 단위 테스트가 진행되는 방법 수준에서만 제대로 작동합니다.


리팩토링 할 때마다 테스트가 변경되면 다른 문제입니다. 단위 테스트는 소프트웨어가 수행하는 작업을 문서화하여 수행한다는 것을 입증 한 다음 기본 구현을 리팩토링 할 때 다시 수행한다는 것을 입증해야합니다 . API가 변경되거나 시스템 설계의 변경에 따라 변경해야 할 메소드가 필요한 경우, 그 결과가 발생합니다. 많은 일이 일어나면 코드를 작성하기 전에 먼저 테스트 작성을 고려하십시오. 이를 통해 전체 아키텍처에 대해 생각하고 이미 설정된 API로 코드를 작성할 수 있습니다.

사소한 코드에 대한 단위 테스트를 작성하는 데 많은 시간을 소비하는 경우

public string SomeProperty { get; set; }

그런 다음 접근 방식을 다시 검토해야합니다. 단위 테스트는 동작 을 테스트하는 것으로 간주되며 위 코드 줄에는 동작이 없습니다. 그러나 속성이 코드의 다른 곳에서 참조되기 때문에 코드 어딘가에서 종속성을 만들었습니다. 그렇게하는 대신 필요한 속성을 매개 변수로 받아들이는 메서드를 작성해보십시오.

public string SomeMethod(string someProperty);

이제 메소드는 외부에 의존하지 않으며 완전히 자체 포함되어 있기 때문에 더 테스트 가능합니다. 물론, 항상 그렇게 할 수는 없지만 코드를 더 테스트 가능한 방향으로 옮기고 이번에는 실제 행동에 대한 단위 테스트를 작성합니다 .


2
단위 테스트와 통합 테스트 서버의 목표가 다르다는 것을 알고 있지만 단위 테스트가 수행하는 모든 공개 호출을 스텁하고 조롱해도 단위 테스트가 어떻게 유용한 지 알 수 없습니다. 나는 스텁과 모의가 아니라면 '코드가 단위 테스트에 의해 요약 된 계약을 이행한다'는 것을 이해할 것이다. 내 단위 테스트는 문자 그대로 테스트중인 메소드 내부의 논리를 반영합니다. 당신 (I)은 실제로 아무것도 테스트하지 않고 코드를보고 테스트로 '변환'합니다. 자동화 및 코드 적용의 어려움과 관련하여 현재 Rails를 수행하고 있으며 둘 다 잘 처리됩니다.
enthrops

2
테스트가 메소드 논리를 반영한 ​​것이라면 잘못하고 있습니다. 단위 테스트는 본질적으로 메소드에 값을 전달하고 리턴 값을 승인하며 해당 리턴 값이 무엇인지에 대한 주장을해야합니다. 이를 수행하기위한 논리는 필요하지 않습니다.
Robert Harvey

2
의미가 있지만 여전히 다른 모든 공개 호출 (db, 현재 사용자 상태와 같은 일부 '전역')을 스텁하고 메소드 논리에 따라 테스트 코드로 끝내야합니다.
enthrops

1
그래서 단위 테스트는 주로 입력 세트-> 예상 결과 세트를 확인하는 격리 된 물건에 대한 것 같아요?
enthrops

1
많은 단위 및 통합 테스트를 만든 경험 (고급 조롱, 통합 테스트 및 해당 테스트에서 사용하는 코드 범위 도구는 말할 것도없고)에 대한 나의 경험은 대부분의 주장과 모순됩니다. 1) "단위 테스트의 목적은 코드는 예상대로 작동합니다. ": 통합 테스트에도 동일하게 적용됩니다. 2) "단위 테스트는 설정하기가 훨씬 쉽다": 아니요, 그렇지 않습니다 (종종 매우 쉬운 통합 테스트입니다). 3) "올바로 사용될 때, 단위 테스트는"테스트 가능한 "코드의 개발을 장려합니다 : 통합 테스트와 동일; (계속)
Rogério

4

모의가있는 단위 테스트는 클래스의 구현이 올바른지 확인하는 것입니다. 테스트중인 코드 종속성의 공용 인터페이스를 조롱합니다. 이렇게하면 클래스 외부의 모든 것을 제어 할 수 있으며 실패한 테스트는 다른 개체 중 하나가 아닌 클래스 내부의 무언가로 인한 것입니다.

또한 구현이 아닌 테스트중인 클래스의 동작을 테스트하고 있습니다. 코드를 리팩터링하면 (새로운 내부 메소드 작성 등) 단위 테스트에 실패하지 않아야합니다. 그러나 공용 메소드의 기능을 변경하는 경우 동작이 변경되었으므로 테스트가 절대 실패해야합니다.

또한 코드를 작성한 후 테스트를 작성하는 것처럼 들립니다. 대신 테스트를 먼저 작성하십시오. 클래스가이 후 작성해야 동작을 요약 해보십시오 최소 테스트가 통과하게하는 코드의 양을.

단위 테스트와 통합 테스트는 코드의 품질을 보장하는 데 유용합니다. 단위 테스트는 각 구성 요소를 분리하여 검사합니다. 통합 테스트는 모든 구성 요소가 올바르게 상호 작용하는지 확인합니다. 테스트 스위트에 두 가지 유형을 모두 갖고 싶습니다.

단위 테스트는 한 번에 하나의 응용 프로그램에 집중할 수 있기 때문에 개발에 도움이되었습니다. 내가 아직 만들지 않은 구성 요소를 조롱. 그들은 내가 찾은 논리의 버그 (단위 테스트에서도)를 문서화하기 때문에 회귀에 좋습니다.

최신 정보

메소드가 호출되는지 확인하는 테스트를 작성하면 메소드가 실제로 호출되는지 확인해야합니다. 특히 테스트를 먼저 작성하는 경우 발생해야하는 방법의 체크리스트가 있습니다. 이 코드는 절차가 매우 많기 때문에 메소드가 호출되는 것 외에는 확인할 것이 없습니다. 향후 변경을 위해 코드를 보호하고 있습니다. 한 메소드를 다른 메소드보다 먼저 호출해야 할 때. 또는 초기 메소드에서 예외가 발생하더라도 메소드가 항상 호출됩니다.

이 방법에 대한 테스트는 변경되지 않거나 변경하는 경우에만 변경 될 수 있습니다. 왜 이것이 나쁜 것입니까? 테스트 사용을 강화하는 데 도움이됩니다. 코드를 변경 한 후 테스트를 수정해야하는 경우 코드를 사용하여 테스트를 변경하는 습관이 생깁니다.


그리고 메소드가 개인을 호출하지 않으면 단위 테스트에 아무런 의미가 없습니다. 맞습니까?
enthrops

메소드가 개인용 인 경우 명시 적으로 테스트하지 않고 공용 인터페이스를 통해 테스트해야합니다. 동작이 올바른지 확인하기 위해 모든 공용 메소드를 테스트해야합니다.
Schleis

아니요, 공개 메소드가 비공개를 호출하지 않으면 공개 메소드를 테스트하는 것이 합리적입니까?
enthrops

예. 방법이 그렇지 않습니까? 따라서 테스트해야합니다. 테스트 관점에서 나는 그것이 개인적인 것을 사용하고 있는지 모른다. 입력 A를 제공하면 출력 B를
가져와야

예, 메소드는 무언가를 수행하고 무언가는 다른 공용 메소드에 대한 호출입니다 (그리고 그것만). 따라서 '적절하게'테스트하는 방법은 반환 값으로 호출을 스텁 한 다음 메시지 기대치를 설정하는 것입니다. 이 경우 정확히 무엇을 테스트하고 있습니까? 올바른 전화를 걸었습니까? 글쎄, 당신은 그 방법을 썼고 그것을보고 정확히 무엇을 볼 수 있습니다. 단위 테스트는 '입력-> 출력'과 같이 사용해야하는 분리 된 방법에 더 적합하다고 생각하므로 많은 예제를 설정 한 다음 리팩터링 할 때 회귀 테스트를 수행 할 수 있습니다.
열광

3

구성 요소 테스트의 힘을 발견 할 때까지 비슷한 질문이있었습니다. 요컨대, 기본적으로 모의하지 않고 실제 객체를 사용한다는 것을 제외하고는 단위 테스트와 동일합니다 (이상적으로 의존성 주입을 통해).

이렇게하면 좋은 코드 적용 범위로 강력한 테스트를 신속하게 만들 수 있습니다. 모의를 항상 업데이트 할 필요가 없습니다. 100 % 모의 단위 테스트보다 정확도가 떨어질 수 있지만 절약하는 시간과 비용이이를 보완합니다. 모의 품 또는 고정물을 사용해야하는 유일한 것은 스토리지 백엔드 또는 외부 서비스입니다.

실제로, 과도하게 조롱하는 것은 반 패턴입니다. TDD 반 패턴 과 목 은 악 합니다.


0

op가 이미 답을 표시했지만 여기에 2 센트를 추가하고 있습니다.

단위 테스트와 통합 테스트 (시간 오버 헤드 제외)의 결정적인 이점은 무엇입니까?

또한

단위 테스트를 "적절한"방법으로 수행 할 때, 즉 모든 공중 전화를 스터 빙하고 사전 설정 값 또는 모의를 반환하면 실제로 아무것도 테스트하지 않는 것 같습니다.

도움이되었지만 정확히 OP가 요구 한 것은 아닙니다.

단위 테스트는 작동하지만 여전히 버그가 있습니까?

스위트 테스트에 대한 나의 작은 경험을 통해, 단위 테스트 는 항상 클래스의 가장 기본적인 메소드 레벨 기능을 테스트해야 한다는 것을 이해합니다 . 내 의견으로 는 공개, 개인 또는 내부의 모든 방법 은 전용 단위 테스트를 받아야합니다. 최근의 경험에서도 다른 작은 개인용 메서드를 호출하는 공용 메서드가있었습니다. 따라서 두 가지 접근 방식이있었습니다.

  1. 비공개 메소드에 대한 단위 테스트를 작성하지 마십시오.
  2. 개인 메소드에 대한 단위 테스트를 작성하십시오.

논리적으로 생각하면 개인 방법을 사용하는 요점은 다음과 같습니다. 주 공용 방법이 너무 커지거나 복잡해집니다. 이 문제를 해결하기 위해 현명하게 리팩터링하고 별도의 개인 메소드가 될 수있는 작은 코드를 작성하여 기본 공개 메소드의 부피를 줄입니다. 이 개인용 메소드는 나중에 재사용 될 수 있음을 명심하여 리팩토링하십시오. 그 개인의 방법에 따라 다른 공개 방법이 없지만 미래에 대해 아는 사람이있을 수 있습니다.


개인 메소드가 다른 많은 공용 메소드에서 재사용되는 경우를 고려하십시오.

따라서 접근 방식 1을 선택했다면 단위 테스트를 복제했을 것입니다. 공용 메소드의 각 분기 및 개인 메소드에 대해 단위 테스트가 많았 기 때문에 복잡했습니다.

접근 방식 2를 선택한 경우 : 단위 테스트 용으로 작성된 코드는 상대적으로 적으며 테스트하기가 훨씬 쉽습니다.


전용 메소드를 재사용하지 않는 경우를 고려하여 해당 메소드에 대해 별도의 단위 테스트를 작성할 필요는 없습니다.

로에 관해서는 통합 테스트는 우려하고있다, 그들은 철저한와 높은 수준의 더 경향이있다. 학생들은 입력을 받으면 모든 클래스가이 최종 결론에 도달해야한다고 알려줄 것입니다. 통합 테스트의 유용성에 대한 자세한 내용은 언급 된 링크를 참조하십시오.

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