문제

나는 여러분 대부분이 자동화된 테스트를 많이 작성하고 있으며 단위 테스트를 할 때 몇 가지 일반적인 함정에 부딪힌 적이 있을 것이라고 확신합니다.

내 질문은 미래의 문제를 피하기 위해 테스트 작성에 대한 행동 규칙을 따르는 것입니까?좀 더 구체적으로 말하자면:무엇입니까? 좋은 단위 테스트의 특징 아니면 테스트를 어떻게 작성하나요?

언어에 구애받지 않는 제안이 권장됩니다.

도움이 되었습니까?

해결책

소스를 연결하는 것부터 시작하겠습니다. JUnit을 사용한 Java의 실용적인 단위 테스트 (C#-Nunit 버전도 있습니다..근데 나 이거 갖고 있어..대부분의 경우 불가지론적입니다.추천합니다.)

좋은 테스트는 다음과 같습니다 여행 (두문자어는 접착력이 충분하지 않습니다. 책에 있는 치트 시트의 인쇄물이 있는데 이 내용이 맞는지 확인하기 위해 꺼내야 했습니다..)

  • 자동적 인 :테스트 호출과 PASS/FAIL 결과 확인은 자동으로 이루어져야 합니다.
  • 철저한:적용 범위;버그는 코드의 특정 영역에 집중되는 경향이 있지만 모든 주요 경로와 시나리오를 테스트해야 합니다.테스트되지 않은 영역을 알아야 하는 경우 도구를 사용하세요.
  • 반복 가능:테스트는 매번 동일한 결과를 산출해야 합니다.매번.테스트는 제어할 수 없는 매개변수에 의존해서는 안 됩니다.
  • 독립적인:매우 중요.
    • 테스트는 한 가지만 테스트해 보세요 한 번에.여러 개의 어설션은 모두 하나의 기능/동작을 테스트하는 한 괜찮습니다.테스트가 실패하면 문제의 위치를 ​​정확히 찾아내야 합니다.
    • 테스트 서로 의지하면 안 된다 - 격리되었습니다.테스트 실행 순서에 대한 가정은 없습니다.설정/해제를 적절하게 사용하여 각 테스트 전에 '깨끗한 상태'를 보장합니다.
  • 전문적인:장기적으로는 프로덕션만큼 많은 테스트 코드를 가지게 되므로(그 이상은 아니더라도) 테스트 코드에 대해 동일한 좋은 디자인 표준을 따르십시오.의도를 드러내는 이름, 중복 없음, 좋은 이름을 사용한 테스트 등을 갖춘 잘 분해된 메소드 클래스.

  • 좋은 테스트도 실행됩니다. 빠른.실행하는 데 0.5초 이상 걸리는 모든 테스트..작업이 필요합니다.테스트 스위트를 실행하는 데 시간이 오래 걸릴수록..덜 자주 실행됩니다.개발자가 실행 사이에 몰래 시도할 변경 사항이 많을수록..뭐라도 깨지면..어떤 변경 사항이 원인인지 파악하는 데 시간이 더 오래 걸립니다.

2010-08 업데이트:

  • 읽기 가능 :이는 Professional의 일부로 간주될 수 있지만 아무리 강조해도 지나치지 않습니다.엄격한 테스트는 팀에 속하지 않은 사람을 찾아서 몇 분 내에 테스트 중인 동작을 파악하도록 요청하는 것입니다.테스트는 프로덕션 코드와 마찬가지로 유지 관리되어야 하므로 더 많은 노력이 필요하더라도 읽기 쉽게 만드세요.테스트는 대칭적(패턴 따르기)이어야 하고 간결해야 합니다(한 번에 하나의 동작 테스트).일관된 명명 규칙을 사용하세요(예:TestDox 스타일)."부수적인 세부 사항"으로 테스트를 복잡하게 만들지 마십시오.미니멀리스트가 되어보세요.

이 외에도 대부분의 다른 지침은 저혜택 업무를 줄이는 지침입니다.예를 들어'자신이 소유하지 않은 코드를 테스트하지 마세요'(예:타사 DLL).getter와 setter를 테스트하지 마세요.비용 대비 이익 비율이나 결함 가능성을 주시하십시오.

다른 팁

  1. 엄청난 테스트를 작성하지 마세요. '단위 테스트'의 '단위'에서 알 수 있듯이 각각을 다음과 같이 만듭니다. 원자 그리고 외딴 가능한 한.꼭 필요한 경우 일반적인 사용자 환경을 수동으로 너무 많이 다시 만드는 대신 모의 객체를 사용하여 전제 조건을 만드세요.
  2. 분명히 작동하는 것을 테스트하지 마십시오. 타사 공급업체, 특히 코딩하는 프레임워크의 핵심 API를 제공하는 공급업체의 클래스를 테스트하지 마세요.예를 들어 공급업체의 Hashtable 클래스에 항목을 추가하는 것을 테스트하지 마세요.
  3. 코드 검사 도구 사용을 고려해보세요. 아직 테스트하지 않은 엣지 케이스를 발견하는 데 도움이 되는 Ncover와 같은 것입니다.
  4. 테스트를 작성해 보세요 ~ 전에 구현. 테스트를 구현이 준수할 사양에 가깝다고 생각하세요.참조.또한 테스트 중심 개발의 보다 구체적인 분야인 행동 중심 개발도 있습니다.
  5. 일관성을 유지하십시오. 일부 코드에 대해서만 테스트를 작성한다면 거의 유용하지 않습니다.팀으로 일하고 다른 사람들 중 일부 또는 전부가 테스트를 작성하지 않는다면 그다지 유용하지 않습니다.자신과 다른 모든 사람에게 중요성을 확신시키십시오(그리고 시간 절약 속성) 테스트를 진행하거나 신경쓰지 마세요.

여기에 있는 대부분의 답변은 실제로 테스트 자체(방법)를 작성하기보다는 일반적인 단위 테스트 모범 사례(언제, 어디서, 왜, 무엇을)를 다루는 것 같습니다."어떻게" 부분에 대한 질문이 꽤 구체적인 것 같아서, 제가 회사에서 진행했던 "브라운백" 프레젠테이션에서 발췌하여 이 글을 올려야겠다고 생각했습니다.

Womp의 5가지 글쓰기 테스트 법칙:


1.길고 설명이 포함된 테스트 방법 이름을 사용하십시오.

   - Map_DefaultConstructorShouldCreateEmptyGisMap()
   - ShouldAlwaysDelegateXMLCorrectlyToTheCustomHandlers()
   - Dog_Object_Should_Eat_Homework_Object_When_Hungry()

2.테스트를 다음과 같이 작성하세요. 편곡/연기/주장 스타일.

  • 이 조직의 전략은 한동안 이루어졌으며 많은 것들을 불렀지 만, "AAA"약어의 도입은 최근이를 해결하기에 좋은 방법이었습니다.AAA 스타일과 일치하는 모든 테스트를 쉽게 읽고 유지 관리 할 수 ​​있습니다.

삼.항상 Assert에 실패 메시지를 제공하세요.

Assert.That(x == 2 && y == 2, "An incorrect number of begin/end element 
processing events was raised by the XElementSerializer");
  • 러너 애플리케이션에서 무엇이 실패했는지 명확하게 보여주는 간단하면서도 보람 있는 연습입니다.메시지를 제공하지 않으면 일반적으로 실패 출력에 "예상된 사실, 거짓이었습니다"와 같은 내용이 표시되므로 실제로 테스트를 읽어 무엇이 잘못되었는지 알아내야 합니다.

4.테스트 이유를 댓글로 달아주세요 – 비즈니스 가정은 무엇입니까?

  /// A layer cannot be constructed with a null gisLayer, as every function 
  /// in the Layer class assumes that a valid gisLayer is present.
  [Test]
  public void ShouldNotAllowConstructionWithANullGisLayer()
  {
  }
  • 이것은 분명해 보일지 모르지만,이 관행은 처음부터 시험의 이유를 이해하지 못하는 사람들로부터 시험의 무결성을 보호 할 것입니다.테스트가 검증 한 가정을 이해하지 못했기 때문에 많은 테스트가 완전히 제거되거나 수정되는 것을 보았습니다.
  • 테스트가 사소하거나 메소드 이름이 충분히 설명적인 경우 주석을 끄는 것이 허용 될 수 있습니다.

5.모든 테스트는 항상 접촉하는 리소스의 상태를 되돌려야 합니다.

  • 실제 자원을 다루지 않도록 가능한 한 모의를 사용하십시오.
  • 정리는 테스트 수준에서 수행해야합니다.테스트는 실행 순서에 의존하지 않아야합니다.

이러한 목표를 염두에 두십시오(Meszaros의 xUnit Test Patterns 책에서 수정됨)

  • 테스트는 위험을 줄여서 소개하지 않아야합니다.
  • 테스트는 실행하기 쉬워야 합니다.
  • 시스템이 그들 주위에 발전함에 따라 테스트는 유지하기 쉬워야합니다.

이를 더 쉽게 만드는 몇 가지 사항:

  • 테스트는 한 가지 이유 때문에 실패해야합니다.
  • 테스트는 한 가지만 테스트해야 합니다.
  • 테스트 종속성 최소화 (데이터베이스, 파일, UI 등에 대한 종속성 없음)

xUnit 프레임워크로도 통합 테스트를 수행할 수 있다는 것을 잊지 마세요. 하지만 통합 테스트와 단위 테스트는 별도로 유지하세요.

테스트는 격리되어야 합니다.하나의 테스트가 다른 테스트에 의존해서는 안 됩니다.더욱이 테스트는 외부 시스템에 의존해서는 안 됩니다.즉, 테스트 당신의 코드가 의존하는 코드가 아니라 코드입니다. 통합 또는 기능 테스트의 일부로 이러한 상호 작용을 테스트할 수 있습니다.

훌륭한 단위 테스트의 몇 가지 속성:

  • 테스트가 실패하면 문제가 어디에 있는지 즉시 알 수 있어야 합니다.문제를 추적하기 위해 디버거를 사용해야 한다면 테스트가 충분히 세부적이지 않은 것입니다.테스트당 정확히 하나의 어설션을 갖는 것이 도움이 됩니다.

  • 리팩터링할 때 어떤 테스트도 실패해서는 안 됩니다.

  • 테스트는 실행하는 데 주저함이 없을 정도로 매우 빠르게 실행되어야 합니다.

  • 모든 테스트는 항상 통과해야 합니다.비결정적 결과가 없습니다.

  • 단위 테스트는 프로덕션 코드와 마찬가지로 잘 구성되어 있어야 합니다.

@알로토르:라이브러리가 외부 API에서만 단위 테스트를 수행해야 한다고 제안한다면 나는 이에 동의하지 않습니다.외부 호출자에게 노출하지 않는 클래스를 포함하여 각 클래스에 대한 단위 테스트를 원합니다.(하지만, 비공개 메소드에 대한 테스트를 작성해야 한다고 생각되면 리팩토링해야 합니다.)


편집하다:"테스트당 하나의 어설션"으로 인한 중복에 대한 의견이 있었습니다.특히, 시나리오를 설정하기 위한 코드가 있고 이에 대해 여러 어설션을 만들고 싶지만 테스트당 하나의 어설션만 있는 경우 여러 테스트에 걸쳐 설정을 복제할 수 있습니다.

나는 그런 접근 방식을 취하지 않습니다.대신 테스트 픽스처를 사용합니다. 시나리오별.대략적인 예는 다음과 같습니다.

[TestFixture]
public class StackTests
{
    [TestFixture]
    public class EmptyTests
    {
        Stack<int> _stack;

        [TestSetup]
        public void TestSetup()
        {
            _stack = new Stack<int>();
        }

        [TestMethod]
        [ExpectedException (typeof(Exception))]
        public void PopFails()
        {
            _stack.Pop();
        }

        [TestMethod]
        public void IsEmpty()
        {
            Assert(_stack.IsEmpty());
        }
    }

    [TestFixture]
    public class PushedOneTests
    {
        Stack<int> _stack;

        [TestSetup]
        public void TestSetup()
        {
            _stack = new Stack<int>();
            _stack.Push(7);
        }

        // Tests for one item on the stack...
    }
}

당신이 추구하는 것은 테스트 중인 클래스의 동작을 묘사하는 것입니다.

  1. 예상되는 행동의 검증.
  2. 오류 사례 확인.
  3. 클래스 내의 모든 코드 경로를 포괄합니다.
  4. 클래스 내의 모든 멤버 함수를 실행합니다.

기본 의도는 학급의 행동에 대한 자신감을 높이는 것입니다.

이는 코드를 리팩터링할 때 특히 유용합니다.마틴 파울러(Martin Fowler)는 흥미로운 점을 가지고 있습니다. 기사 그의 웹사이트에서의 테스트에 관해.

HTH.

건배,

테스트는 원래 실패해야 합니다.그런 다음 이를 통과시키는 코드를 작성해야 합니다. 그렇지 않으면 버그가 있어 항상 통과하는 테스트를 작성할 위험이 있습니다.

나는 앞서 언급한 오른쪽 BICEP 약어를 좋아합니다. 실용적인 단위 테스트 책:

  • 오른쪽:결과는요? 오른쪽?
  • :모두 기본 조건이 맞나요?
  • :확인해볼까요? 반대관계?
  • :우리는 할 수 있습니까? 다른 방법을 사용하여 결과를 확인합니까?
  • 이자형:강제로 할 수 있을까? 이자형오류 조건이 발생합니까?
  • :~이다 성능 특성이 범위 내에서?

개인적으로 나는 올바른 결과를 얻었는지 확인하고(덧셈 함수에서 1+1은 2를 반환해야 함) 생각할 수 있는 모든 경계 조건을 시도함으로써(예: 합계가 계산되는 두 숫자 사용) 꽤 멀리 갈 수 있다고 생각합니다. 추가 기능의 정수 최대 값보다 크므로) 네트워크 오류와 같은 오류 조건을 강제합니다.

좋은 테스트는 유지 관리가 가능해야 합니다.

복잡한 환경에서 이 작업을 수행하는 방법을 아직 찾지 못했습니다.

코드베이스가 수백 1000 또는 수백만 줄의 코드에 도달하기 시작함에 따라 모든 교과서는 팽팽 해지 기 시작합니다.

  • 팀 상호작용이 폭발적으로 증가함
  • 테스트 케이스의 수가 폭발적으로 증가
  • 구성 요소 간의 상호 작용이 폭발합니다.
  • 모든 단위 테스트를 빌드하는 시간은 빌드 시간의 중요한 부분이 됩니다.
  • API 변경은 수백 개의 테스트 사례로 파급될 수 있습니다.생산 코드 변경이 쉬웠음에도 불구하고.
  • 프로세스를 올바른 상태로 순서 지정하는 데 필요한 이벤트 수가 증가하고 결과적으로 테스트 실행 시간이 늘어납니다.

좋은 아키텍처는 상호 작용 폭발의 일부를 제어 할 수 있지만 시스템이 더욱 복잡 해짐에 따라 자동 테스트 시스템이 커지면서 필연적으로 자랍니다.

여기에서 트레이드오프를 처리해야 합니다.

  • 외부 API만 테스트하고 그렇지 않으면 내부를 리팩토링하면 상당한 테스트 사례 재작업이 발생합니다.
  • 캡슐화된 하위 시스템이 더 많은 상태를 유지하므로 각 테스트의 설정 및 해체가 더욱 복잡해집니다.
  • 야간 컴파일 및 자동화된 테스트 실행이 몇 시간으로 늘어납니다.
  • 컴파일 및 실행 시간이 길어지면 디자이너가 모든 테스트를 실행하지 않거나 실행하지 않을 것임을 의미합니다.
  • 테스트 실행 시간을 줄이려면 설정 및 해제를 줄이기 위한 테스트 순서 지정을 고려하세요.

또한 다음 사항도 결정해야 합니다.

코드베이스에서 테스트 사례를 어디에 저장합니까?

  • 테스트 케이스를 어떻게 문서화합니까?
  • 테스트 케이스 유지 관리를 절약하기 위해 테스트 픽스처를 재사용할 수 있습니까?
  • 야간 테스트 케이스 실행이 실패하면 어떻게 되나요?분류는 누가 합니까?
  • 모의 개체를 어떻게 유지합니까?자체적인 모의 로깅 API를 사용하는 20개의 모듈이 모두 있는 경우 API를 변경하면 빠르게 파급됩니다.테스트 케이스가 변경될 뿐만 아니라 20개의 모의 객체도 변경됩니다.이 20개의 모듈은 다양한 팀에서 수년에 걸쳐 작성되었습니다.이는 전형적인 재사용 문제입니다.
  • 개인과 팀은 자동화된 테스트의 가치를 이해하지만 다른 팀이 수행하는 방식은 마음에 들지 않습니다.:-)

나는 영원히 계속할 수 있지만 내 요점은 다음과 같습니다.

테스트는 유지 관리가 가능해야 합니다.

나는 이 원칙을 예전에 다루었습니다. 이 MSDN 매거진 기사 모든 개발자가 읽어야 할 중요한 내용이라고 생각합니다.

내가 "좋은" 단위 테스트를 정의하는 방법은 다음과 같은 세 가지 속성을 갖는 경우입니다.

  • 읽을 수 있습니다(이름 지정, 주장, 변수, 길이, 복잡성..).
  • 유지 관리가 가능합니다(논리 없음, 과도하게 지정되지 않음, 상태 기반, 리팩토링됨..)
  • 신뢰할 수 있습니다(통합 테스트가 아닌 격리된 올바른 테스트를 수행합니다..).
  • 단위 테스트는 단위의 외부 API를 테스트할 뿐 내부 동작을 테스트해서는 안 됩니다.
  • TestCase의 각 테스트는 이 API 내에서 하나의 메서드만 테스트해야 합니다.
    • 실패 사례에 대해서는 추가 테스트 사례가 포함되어야 합니다.
  • 테스트 범위를 테스트합니다.장치가 테스트되면 이 장치 내부의 라인이 100% 실행되어야 합니다.

제이 필즈는 좋은 조언을 많이 해주세요 단위 테스트 작성에 관한 내용이 있습니다. 가장 중요한 조언을 요약한 게시물입니다..거기에서 당신은 당신의 상황에 대해 비판적으로 생각하고 조언이 당신에게 가치가 있는지 판단해야 한다는 것을 읽게 될 것입니다.여기에서 수많은 놀라운 답변을 얻을 수 있지만 상황에 가장 적합한 답변을 결정하는 것은 귀하의 몫입니다.시도해 보고 냄새가 좋지 않으면 리팩토링하면 됩니다.

친절한 감사

사소한 두 줄 방법이 작동할 것이라고 결코 가정하지 마십시오.빠른 단위 테스트를 작성하는 것은 누락된 Null 테스트, 잘못 배치된 빼기 기호 및/또는 미묘한 범위 지정 오류가 필연적으로 지금보다 처리할 시간이 훨씬 적을 때 발생하는 것을 방지할 수 있는 유일한 방법입니다.

두 번째로 "A TRIP" 답변을 사용하겠습니다. 단, 테스트는 서로 의존해야 합니다!!!

왜?

DRY - 반복하지 마세요 - 테스트에도 적용됩니다!테스트 종속성은 1) 설정 시간을 절약하고, 2) 고정 장치 리소스를 절약하고, 3) 오류를 정확히 찾아내는 데 도움이 될 수 있습니다.물론, 테스트 프레임워크가 최고 수준의 종속성을 지원하는 경우에만 해당됩니다.그렇지 않으면 나는 그들이 나쁘다는 것을 인정합니다.

후속 조치 http://www.iam.unibe.ch/~scg/Research/JExample/

단위 테스트는 모의 객체나 모의 데이터를 기반으로 하는 경우가 많습니다.나는 세 가지 종류의 단위 테스트를 작성하는 것을 좋아합니다.

  • "일시적인" 단위 테스트:그들은 자신만의 모의 개체/데이터를 만들고 그것으로 기능을 테스트하지만 모든 것을 파괴하고 흔적을 남기지 않습니다(테스트 데이터베이스에 데이터가 없는 것처럼).
  • "지속적인" 단위 테스트:그들은 나중에 자체 단위 테스트를 위해 고급 기능에 필요한 객체/데이터를 생성하는 코드 내에서 기능을 테스트합니다(고급 기능이 자체 모의 객체/데이터 세트를 매번 다시 생성하는 것을 피함)
  • "지속 기반" 단위 테스트:영구 단위 테스트에 의해 이미 존재하는(다른 단위 테스트 세션에서 생성되었기 때문에) 모의 객체/데이터를 사용하는 단위 테스트.

요점은 재생을 피하는 것입니다 모든 것 모든 기능을 테스트할 수 있도록 말이죠.

  • 나는 모든 모의 객체/데이터가 이미 거기에 있기 때문에 세 번째 종류를 매우 자주 실행합니다.
  • 나는 모델이 바뀔 때마다 두 번째 종류를 실행합니다.
  • 나는 아주 기본적인 기능을 가끔씩 확인하고 기본 회귀를 확인하기 위해 첫 번째 것을 실행합니다.

기능 테스트와 성능 테스트라는 두 가지 테스트 유형에 대해 생각하고 다르게 처리하십시오.

각각에 대해 서로 다른 입력과 측정항목을 사용합니다.각 테스트 유형마다 다른 소프트웨어를 사용해야 할 수도 있습니다.

나는 다음에 설명된 일관된 테스트 명명 규칙을 사용합니다. Roy Osherove의 단위 테스트 명명 표준 지정된 테스트 사례 클래스의 각 메서드에는 다음과 같은 명명 스타일 MethodUnderTest_Scenario_ExpectedResult가 있습니다.

    첫 번째 테스트 이름 섹션은 테스트 중인 시스템의 메서드 이름입니다.
    다음은 테스트 중인 특정 시나리오입니다.
    마지막으로 해당 시나리오의 결과입니다.

각 섹션은 Upper Camel Case를 사용하며 언더 스코어로 구분됩니다.

테스트를 실행할 때 테스트가 테스트 중인 메서드 이름별로 그룹화되는 것이 유용하다는 것을 알았습니다.그리고 다른 개발자가 테스트 의도를 이해할 수 있도록 하는 규칙이 있습니다.

또한 테스트 중인 메서드가 오버로드된 경우 메서드 이름에 매개변수를 추가합니다.

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