정말로 단위 테스트 프레임 워크가 필요합니까? 애플리케이션을위한 대규모 단위 테스트 스위트를 사용하고

현재는 C ++ 애플리케이션을위한 대규모 단위 테스트 스위트를 사용하고 있습니다. 그러나 단위 테스트 프레임 워크는 사용하지 않습니다. 그들은 기본적으로 어설 션과 cout을 감싸는 C 매크로를 사용합니다. 다음과 같은 것 :

VERIFY(cond) if (!(cond)) {std::cout << "unit test failed at " << __FILE__ << "," << __LINE__; asserst(false)}

그런 다음 각 테스트에 대한 함수를

void CheckBehaviorYWhenXHappens()
{
    // a bunch of code to run the test
    //
    VERIFY(blah != blah2);
    // more VERIFY's as needed
}

CI 서버는 “단위 테스트 실패”를 선택하고 빌드에 실패하여 개발자에게 메시지를 이메일로 보냅니다.

설정 코드가 중복 된 경우 프로덕션 환경에있는 다른 중복 코드와 마찬가지로 리팩터링하기 만하면됩니다. 우리는 그것을 도우미 함수 뒤에 감싸고, 자주 사용되는 시나리오를 설정하는 테스트 클래스를 만듭니다.

CppUnit 및 부스트 단위 테스트와 같은 프레임 워크가 있다는 것을 알고 있습니다. 이것들이 어떤 가치를 더하는지 궁금합니다. 이것들이 테이블에 가져다 놓친 것을 놓치고 있습니까? 그들에게서 얻을 수있는 유용한 것이 있습니까? 나는 우리가 가지고있는 것이 단순하고 잘 작동하는 것처럼 보이기 때문에 실제 가치를 추가하지 않는 한 의존성을 추가하는 것을 주저합니다.



답변

다른 사람들이 이미 말했듯이, 당신은 이미 당신 자신의 간단한 집에서 만든 프레임 워크를 가지고 있습니다.

하나 만드는 것이 사소한 것 같습니다. 그러나 단위 테스트 프레임 워크에는 언어에 대한 고급 지식이 있기 때문에 구현하기 쉽지 않은 다른 기능이 있습니다. 필자가 일반적으로 테스트 프레임 워크에서 필요로하는 기능은 다음과 같습니다.

  • 테스트 케이스 자동 수집. 즉, 새로운 테스트 방법을 정의하면 충분합니다. JUnit은 이름이로 시작하고 testNUnit에 [Test]주석 이있는 모든 메소드를 자동으로 수집 합니다. Boost.Test는 BOOST_AUTO_TEST_CASEBOOST_FIXTURE_TEST_CASE매크로를 사용 합니다.

    대부분 편리하지만 개발자가 얻을 수있는 모든 편의는 개발자가 실제로 필요한 테스트를 작성하고 올바르게 연결할 가능성을 높입니다. 지시 사항이 길다면 누군가가 지금 그 중 일부를 놓치게되며 아마도 일부 테스트가 실행되지 않고 아무도 알아 채지 못할 것입니다.

  • 코드를 수정하거나 다시 컴파일하지 않고도 선택한 테스트 사례를 실행할 수 있습니다. 적절한 단위 테스트 프레임 워크를 사용하면 명령 줄에서 실행할 테스트를 지정할 수 있습니다. 단위 테스트에서 디버깅하려면 (많은 개발자에게 가장 중요한 점) 코드를 변경하지 않고도 실행할 테스트를 선택할 수 있어야합니다.

    방금 버그 보고서 # 4211을 받았으며 단위 테스트로 재현 할 수 있다고 가정하십시오. 따라서 하나를 작성하지만 러너에게 해당 테스트 만 실행하도록 지시하는 것보다 실제로 무엇이 잘못되었는지 디버그 할 수 있습니다.

  • 검사 자체를 수정하지 않고 테스트 사례별로 테스트 예상 실패를 표시 할 수 있습니다. 우리는 실제로 이것을 얻기 위해 직장에서 프레임 워크를 전환했습니다.

    적절한 크기의 테스트 스위트에는 테스트 기능이 아직 구현되지 않았고 아직 완료되지 않았기 때문에 아직 테스트를 수행 할 시간이 없었기 때문에 실패한 테스트가 있습니다. 테스트를 예상 실패로 표시 할 수 없으면 규칙적인 테스트가있을 때 다른 실패를 발견하지 못하므로 테스트의 주된 목적을 달성하지 못합니다.


답변

집에서 만든 프레임 워크를 이미 사용하고있는 것 같습니다.

보다 대중적인 프레임 워크의 부가 가치는 무엇입니까? 그들이 추가하는 가치는 회사 외부 사람들과 코드를 교환해야 할 때 알려져 있고 널리 사용되는 프레임 워크를 기반으로하기 때문에 코드를 교환 할 수 있다는 것 입니다.

반면에 집에서 만든 프레임 워크는 코드를 공유하지 않거나 프레임 워크 자체를 제공하도록 강요하여 프레임 워크 자체의 성장에 번거로울 수 있습니다.

설명과 단위 테스트 프레임 워크없이 동료에게 코드를 제공하면 코드를 컴파일 할 수 없습니다.

집에서 만든 프레임 워크의 두 번째 단점은 호환성 입니다. 널리 사용되는 단위 테스트 프레임 워크는 다른 IDE, 버전 제어 시스템 등과의 호환성을 보장하는 경향이 있습니다. 현재로서는 중요하지 않지만 언젠가 CI 서버에서 무언가를 변경하거나 마이그레이션해야하는 경우에는 어떻게됩니까? 새로운 IDE 또는 새로운 VCS에? 바퀴를 재발 명 하시겠습니까?

마지막으로 더 큰 프레임 워크는 언젠가 자신의 프레임 워크에서 구현해야하는 더 많은 기능 을 제공 합니다. Assert.AreEqual(expected, actual)항상 충분하지는 않습니다. 필요한 경우 :

  • 정밀 측정?

    Assert.AreEqual(3.1415926535897932384626433832795, actual, 25)
    
  • 너무 오래 작동하면 무효 시험? 비동기식 프로그래밍을 용이하게하는 언어에서도 타임 아웃을 다시 구현하는 것은 간단하지 않을 수 있습니다.

  • 예외가 발생할 것으로 예상되는 방법을 테스트합니까?

  • 더 우아한 코드가 있습니까?

    Assert.Verify(a == null);
    

    괜찮지 만 다음 줄을 쓰려는 의도가 더 표현 적이 지 않습니까?

    Assert.IsNull(a);
    

답변

다른 사람들이 이미 말했듯이 이미 집에서 만든 프레임 워크가 있습니다.

다른 테스트 프레임 워크를 사용할 수있는 유일한 이유는 업계의 “공통 지식”관점에서 비롯된 것입니다. 새로운 개발자는 집에서 만든 방식을 배울 필요가 없습니다 (매우 단순 해 보이지만).

또한 다른 테스트 프레임 워크에는 더 많은 기능을 활용할 수 있습니다.


답변

간단한 프레임 워크라도 이미 프레임 워크가 있습니다.

내가 본 더 큰 프레임 워크의 주요 장점은 다양한 종류의 주장 (어설 션 주장과 같은), 단위 테스트에 대한 논리적 순서 및 단위 테스트의 하위 집합 만 실행할 수 있다는 것입니다. 시간. 또한 가능한 경우 xUnit 테스트 패턴을 따르는 것이 좋습니다 (예 : setUP () 및 tearDown ()). 물론, 그것은 당신을 상기 프레임 워크에 고정시킵니다. 일부 프레임 워크는 다른 프레임 워크보다 모의 통합이 더 뛰어납니다 (예 : Google 모의 및 테스트).

모든 단위 테스트를 새로운 프레임 워크로 리팩토링하는 데 얼마나 걸립니까? 며칠 또는 몇 주 정도는 가치가 있지만 더 많지는 않을 것입니다.


답변

내가 보는 방식에 따라 두 가지 모두 장점이 있으며 “불이익”(sic)에 있습니다.

장점은 편안하다고 느끼고 자신에게 맞는 시스템을 가지고 있다는 것입니다. 제품의 유효성을 확인하게되어 기쁩니다. 다른 프레임 워크를 사용하는 제품에 대해 모든 테스트를 변경하려고하면 비즈니스 가치가 없을 것입니다. 코드를 리팩터링 할 수 있고 테스트에서 변경 사항을 적용하는 경우 또는 테스트를 수정할 수 있고 기존 코드가 리팩터링 될 때까지 테스트에 실패하면 모든베이스를 다룰 수 있습니다. 하나…

잘 설계된 단위 테스트 API를 사용하는 것의 장점 중 하나는 대부분의 최신 IDE에서 많은 기본 지원이 있다는 것입니다. 이것은 Visual Studio 사용자를 비웃는 하드 코어 VI 및 emacs 사용자에게는 영향을 미치지 않지만, 훌륭한 IDE를 사용하는 사용자에게는 테스트를 디버깅하고 실행할 수 있습니다 IDE 자체. 이것은 좋지만 사용하는 프레임 워크에 따라 더 큰 이점이 있으며 코드를 테스트하는 데 사용되는 언어 입니다.

language 라고 말할 때 나는 프로그래밍 언어에 대해 말하는 것이 아니라 테스트 코드를 이야기처럼 읽을 수 있도록 유창한 구문으로 묶인 풍부한 세트 단어에 대해 이야기하고 있습니다. 특히 저는 BDD 프레임 워크 사용을 옹호했습니다 . 내가 가장 좋아하는 DotNet BDD API는 StoryQ입니다.그러나 동일한 기본 목적을 가진 여러 가지가 있습니다. 요구 사항 문서에서 개념을 가져 와서 스펙에서 작성되는 방식과 유사한 방식으로 코드로 작성하는 것입니다. 그러나 테스트에서 모든 개별 명령문을 인터셉트하고 해당 명령문이 성공적으로 실행되었는지 또는 실패했는지를 표시함으로써 실제로 좋은 API가 더욱 발전합니다. 일찍 반환하지 않고 전체 테스트가 실행되는 것을 볼 때 매우 유용합니다. 즉, 전체 호출을 디코딩하지 않고도 실패한 테스트 부분에만주의를 기울여야하기 때문에 디버깅 노력이 매우 효율적입니다. 순서. 다른 좋은 점은 테스트 결과에이 모든 정보가 표시된다는 것입니다.

내가 말하는 것에 대한 예로서, 다음을 비교하십시오.

어설 션 사용 :

Assert(variable_A == expected_value_1); // if this fails...
Assert(variable_B == expected_value_2); // ...this will not execute
Assert(variable_C == expected_value_3); // ...and nor will this!

유창한 BDD API 사용 :
(이탤릭체 비트가 기본적으로 메소드 포인터라고 가정하십시오)

WithScenario("Test Scenario")
    .Given(*AConfiguration*) // each method
    .When(*MyMethodToTestIsCalledWith*, variable_A, variable_B, variable_C) // in the
    .Then(*ExpectVariableAEquals*, expected_value_1) // Scenario will
        .And(*ExpectVariableBEquals*, expected_value_2) // indicate if it has
        .And(*ExpectVariableCEquals*, expected_value_3) // passed or failed execution.
    .Execute();

이제 BDD 구문이 더 길고 더 예리하다는 점을 인정하고 이러한 예제는 끔찍하게 고안되었지만 주어진 시스템 동작의 결과로 시스템에서 많은 것들이 변화하는 매우 복잡한 테스트 상황의 경우 BDD 구문은 명확한 것을 제공합니다. 테스트 대상에 대한 설명 및 테스트 구성을 정의한 방법에 대해 설명하고이 코드를 프로그래머가 아닌 사람에게 보여 주면 진행 상황을 즉시 이해할 수 있습니다. 또한 “variable_A”가 두 경우 모두 테스트에 실패하면 Asserts 예제는 문제를 해결하기 전까지 첫 번째 assert를 지나서 실행되지 않지만 BDD API는 체인에서 호출 된 모든 메소드를 차례로 실행하여 성명서의 개별 부분이 잘못되었습니다.

개인적으로이 접근법은 전통적인 xUnit 프레임 워크가 테스트 언어가 고객의 논리적 요구 사항과 동일한 언어라는 점에서 훨씬 더 효과적이라는 것을 알았습니다. 그럼에도 불구하고 필자는 노력을 지원하기 위해 완전한 테스트 API를 만들지 않고도 비슷한 스타일로 xUnit 프레임 워크를 사용할 수 있었으며 어설 션은 여전히 ​​효과적으로 자체적으로 단락되지만 더 깨끗하게 읽습니다. 예를 들어 :

Nunit 사용 :

[Test]
void TestMyMethod()
{
    const int theExpectedValue = someValue;

    GivenASetupToTestMyMethod();

    var theActualValue = WhenIExecuteMyMethodToTest();

    Assert.That(theActualValue, Is.EqualTo(theExpectedValue)); // nice, but it's not BDD
}

단위 테스트 API를 사용하여 탐색하기로 결정한 경우 잠시 동안 수많은 다른 API를 실험하고 접근 방식에 대해 염두에 두어야합니다. 개인적으로 BDD를 옹호하지만 귀하의 비즈니스 요구에 따라 팀의 상황에 따라 다른 것이 필요할 수 있습니다. 그러나 핵심은 기존 시스템을 추측하지 않는 것입니다. 필요한 경우 다른 API를 사용하여 몇 가지 테스트로 기존 테스트를 항상 지원할 수 있지만 모든 것을 동일하게 만들기 위해 큰 테스트 다시 작성을 권장하지는 않습니다. 레거시 코드의 사용이 중단되면 코드와 해당 테스트를 새로운 코드로 쉽게 대체하고 대체 API를 사용하여 테스트 할 수 있으며 이는 실질적인 비즈니스 가치를 제공 할 필요가없는 큰 노력에 투자 할 필요없이 수행 할 수 있습니다. 단위 테스트 API를 사용하는 경우


답변

당신이 가진 것은 간단하고 작업을 완료합니다. 그것이 당신을 위해 작동한다면, 좋습니다. 주류 단위 테스트 프레임 워크 가 필요 하지 않으며 기존 단위 테스트 라이브러리를 새 프레임 워크로 이식하는 작업을 주저 할 것입니다. 단위 테스트 프레임 워크의 가장 큰 가치는 진입 장벽을 줄이는 것입니다. 프레임 워크가 이미 준비되어 있으므로 테스트 작성을 시작하면됩니다. 당신은 그 시점을 지나서 그 혜택을 얻지 못할 것입니다.

주류 프레임 워크를 사용하면 얻을 수있는 또 다른 이점 (그리고 IMO도 미미한 장점)은 새로운 개발자가 사용중인 프레임 워크에 대해 이미 속도를 높일 수 있으므로 교육이 덜 필요하다는 것입니다. 실제로, 당신이 묘사 한 것과 같은 간단한 접근법으로, 이것은 큰 문제가되어서는 안됩니다.

또한 대부분의 주류 프레임 워크에는 프레임 워크에있을 수도 있고 없을 수도있는 특정 기능이 있습니다. 이러한 기능은 배관 코드를 줄이고 테스트 사례를보다 빠르고 쉽게 작성할 수 있도록합니다.

  • 명명 규칙, 주석 / 속성 등을 사용하여 테스트 사례 자동 실행
  • 더욱 다양한 어설 션을 통해 모든 어설 션에 대해 조건부 논리를 작성하거나 예외를 잡아서 해당 유형을 어설 션 할 필요가 없습니다.
  • 테스트 케이스를 분류하여 서브 세트를 쉽게 실행할 수 있습니다.