문제

나는 방금 John Robbins의 'Debugging MS .Net 2.0 Application'을 훑어보기 시작했고 그의 Debug.Assert(...)에 대한 전도에 혼란스러워졌습니다.

그는 잘 구현된 Assert가 오류 조건의 상태를 어느 정도 저장한다는 점을 지적합니다. 예:

Debug.Assert(i > 3, "i > 3", "This means I got a bad parameter");

이제 개인적으로 그가 실제로 합리적인 '비즈니스 논리' 설명 없이 테스트를 다시 작성하는 것을 좋아한다는 것이 나에게는 미친 것처럼 보입니다. 아마도 "i <= 3은 flobittyjam widgitification 프로세스 때문에 절대 발생해서는 안 됩니다"일 것입니다.

그래서 나는 Asserts를 일종의 낮은 수준의 "가정을 보호하자"라고 생각합니다.이것이 디버그에서만 수행하면 되는 테스트라고 생각한다고 가정합니다. 즉,당신은 동료와 미래의 프로그래머로부터 자신을 보호하고 그들이 실제로 테스트하기를 바라고 있습니다.

그러나 내가 이해하지 못하는 것은 그가 계속해서 일반적인 오류 처리 외에 어설션을 사용해야 한다고 말하는 것입니다.이제 내가 상상하는 것은 다음과 같습니다.

Debug.Assert(i > 3, "i must be greater than 3 because of the flibbity widgit status");
if (i <= 3)
{
    throw new ArgumentOutOfRangeException("i", "i must be > 3 because... i=" + i.ToString());
}

오류 조건 테스트의 Debug.Assert 반복을 통해 무엇을 얻었습니까?매우 중요한 계산에 대한 디버그 전용 이중 확인에 대해 이야기하고 있다면 이해할 수 있을 것 같습니다...

double interestAmount = loan.GetInterest();
Debug.Assert(debugInterestDoubleCheck(loan) == interestAmount, "Mismatch on interest calc");

...하지만 확실히 확인할 가치가 있는 매개변수 테스트에서는 얻을 수 없습니다(DEBUG 및 릴리스 빌드 모두에서)...아니면.내가 무엇을 놓치고 있나요?

도움이 되었습니까?

해결책

어설션은 매개변수 확인을 위한 것이 아닙니다.매개변수 검사는 항상 수행되어야 하며(문서 및/또는 사양에 지정된 사전 조건에 따라 정확하게 수행되어야 함) ArgumentOutOfRangeException 필요에 따라 던져집니다.

어설션은 "불가능한" 상황(예: 프로그램 로직에서)을 테스트하기 위한 것입니다. 추정하다 사실이다.어설션은 어떤 이유로든 이러한 가정이 깨졌는지 알려주기 위해 존재합니다.

도움이 되었기를 바랍니다!

다른 팁

주장과 예외 던지기에는 통신 측면이 있습니다.

Name 속성과 ToString 메서드가 있는 User 클래스가 있다고 가정해 보겠습니다.

ToString이 다음과 같이 구현된 경우:

public string ToString()
{
     Debug.Assert(Name != null);
     return Name;
}

Name은 null이 아니어야 하며, 그렇다면 User 클래스에 버그가 있는 것입니다.

ToString이 다음과 같이 구현된 경우:

public string ToString()
{
     if ( Name == null )
     {
          throw new InvalidOperationException("Name is null");
     }

     return Name;
}

Name이 null인 경우 호출자가 ToString을 잘못 사용하고 있으므로 호출하기 전에 이를 확인해야 한다는 의미입니다.

두 가지 모두를 사용한 구현

public string ToString()
{
     Debug.Assert(Name != null);
     if ( Name == null )
     {
          throw new InvalidOperationException("Name is null");
     }

     return Name;
}

Name이 null이면 User 클래스에 버그가 있지만 어쨌든 처리하고 싶다고 말합니다.(사용자는 전화를 걸기 전에 이름을 확인할 필요가 없습니다.) 이것이 로빈스가 권장하는 일종의 안전이라고 생각합니다.

저는 디버그와 디버깅에 대한 지침을 제공하는 데 있어 이 점에 대해 오랫동안 열심히 생각해 왔습니다.테스트 문제와 관련하여 주장합니다.

잘못된 입력, 잘못된 상태, 잘못된 작업 순서 및 기타 가능한 오류 조건으로 클래스를 테스트할 수 있어야 하며 어설션은 다음과 같습니다. 절대 여행.각 주장은 무엇인가를 확인해야 합니다. 언제나 수행된 입력이나 계산에 관계없이 true입니다.

내가 얻은 좋은 경험 법칙은 다음과 같습니다.

  1. Assert는 구성과 관계없이 올바르게 작동하는 강력한 코드를 대체하지 않습니다.그것들은 보완적입니다.

  2. 잘못된 값을 입력하거나 오류 조건을 테스트하는 경우에도 단위 테스트 실행 중에 어설션이 실행되어서는 안 됩니다.코드는 어설션이 발생하지 않고 이러한 조건을 처리해야 합니다.

  3. 어설션이 작동하는 경우(단위 테스트 또는 테스트 중에) 클래스에 버그가 발생합니다.

기타 모든 오류(일반적으로 환경(네트워크 연결 끊김) 또는 오용(발신자가 null 값 전달))의 경우 하드 검사 및 예외를 사용하는 것이 훨씬 더 좋고 이해하기 쉽습니다.예외가 발생하면 호출자는 자신의 잘못일 가능성이 높다는 것을 알게 됩니다.어설션이 발생하면 호출자는 어설션이 있는 코드에 버그가 있을 가능성이 높다는 것을 알게 됩니다.

복제 관련:나는 동의한다.Debug.Assert 및 예외 검사를 사용하여 유효성 검사를 복제하는 이유를 알 수 없습니다.이는 코드에 약간의 잡음을 추가하고 누가 잘못했는지에 대한 혼란을 야기할 뿐만 아니라 반복의 한 형태입니다.

나는 예외를 발생시키는 명시적 검사를 사용합니다. 공공의 그리고 보호됨 개인 메소드에 대한 메소드 및 주장.

일반적으로 명시적 검사는 개인 메서드가 잘못된 값을 보지 않도록 보호합니다.실제로, 주장은 불가능해야 하는 조건을 확인하고 있습니다.어설션이 실행되면 클래스의 공개 루틴 중 하나에 포함된 유효성 검사 논리에 결함이 있음을 알려줍니다.

예외를 잡아서 삼켜서 테스트에서 오류를 볼 수 없게 만들 수 있습니다.Debug.Assert에서는 이런 일이 발생할 수 없습니다.

모든 예외를 포착하는 catch 핸들러를 가져서는 안 되지만 사람들은 어쨌든 그렇게 하며 때로는 피할 수 없는 경우도 있습니다.코드가 COM에서 호출되면 interop 계층은 모든 예외를 포착하여 COM 오류 코드로 변환합니다. 즉, 처리되지 않은 예외는 표시되지 않습니다.Assert는 이 문제를 겪지 않습니다.

또한 예외가 처리되지 않은 경우에도 미니 덤프를 수행하는 것이 더 좋습니다.VB가 C#보다 강력한 한 가지 영역은 예외가 진행 중일 때 예외 필터를 사용하여 미니 덤프를 스냅하고 나머지 예외 처리를 변경하지 않고 그대로 둘 수 있다는 것입니다. 예외 필터 주입에 대한 Gregg Miskelly의 블로그 게시물 C#에서 이를 수행하는 유용한 방법을 제공합니다.

자산에 대한 또 다른 참고 사항 ...코드의 오류 조건을 테스트하는 단위와 제대로 상호 작용하지 않습니다.단위 테스트에 대한 어설션을 끄는 래퍼를 갖는 것이 좋습니다.

IMO에서는 개발 시간만 손실됩니다.적절하게 구현된 예외는 무슨 일이 일어났는지 명확하게 보여줍니다.나는 보았다 너무 많은 모호한 "어설션 실패:i < 10" 오류.나는 주장을 임시 해결책으로 본다.내 생각에는 프로그램의 최종 버전에는 어떤 주장도 있어서는 안 됩니다.실제로 나는 빠르고 더러운 검사를 위해 어설션을 사용했습니다.코드의 최종 버전은 잘못된 상황을 고려하고 그에 따라 동작해야 합니다.나쁜 일이 발생하면 두 가지 선택이 있습니다:처리하거나 그대로 두십시오.잘못된 매개변수가 전달되면 함수는 의미 있는 설명과 함께 예외를 발생시켜야 합니다.유효성 검사 논리가 중복되는 점은 없습니다.

Assert의 올바른 사용 예:

Debug.Assert(flibbles.count() < 1000000, "too many flibbles"); // indicate something is awry
log.warning("flibble count reached " + flibbles.count()); // log in production as early warning

나는 개인적으로 Assert가 그래야 한다고 생각합니다. 오직 밖에 뭔가가 있다는 것을 알 때 사용됩니다. 바람직한 하지만 계속하는 것이 합리적으로 안전하다고 확신할 수 있습니다.다른 모든 상황에서는(내가 생각하지 못한 상황을 자유롭게 지적하세요) 예외를 사용하여 강력하고 빠르게 실패합니다.

나에게 있어서 가장 중요한 절충점은 손상을 방지하고 문제 해결을 더 쉽게 하기 위해 예외를 사용하여 라이브/프로덕션 시스템을 중단할지, 아니면 테스트/디버그 버전에서 계속해서 눈에 띄지 않게 허용해서는 안 되지만 발생할 수 있는 상황에 직면했는지 여부입니다. 생산을 계속할 수 있습니다(물론 경고 기록).

참조. http://c2.com/cgi/wiki?FailFastJava 질문에서 복사 및 수정되었습니다. 예외 대 주장

여기는 2센트 단위입니다.

내 생각에 가장 좋은 방법은 단언문과 예외를 모두 사용하는 것입니다.두 가지 방법의 주요 차이점은 Assert 문을 응용 프로그램 텍스트(정의, 조건 속성...)에서 쉽게 제거할 수 있는 반면, 던져진 예외는 제거하기 어려운 조건 코드에 (팁적으로) 종속되는 경우입니다( 전처리기 조건문이 있는 다중 섹션).

모든 애플리케이션 예외는 올바르게 처리되어야 하며, 주장은 알고리즘 개발 및 테스트 중에만 충족되어야 합니다.

널(null) 객체 참조를 루틴 매개변수로 전달하고 이 값을 사용하면 널 포인터 예외가 발생합니다.물론:왜 주장을 작성해야합니까?이 경우에는 시간 낭비입니다.하지만 클래스 루틴에 사용되는 비공개 클래스 멤버는 어떻습니까?이러한 값이 어딘가에 설정된 경우 null 값이 설정되어 있는지 어설션으로 확인하는 것이 좋습니다.그 이유는 멤버를 사용할 때 널 포인터 예외가 발생하지만 값이 어떻게 설정되었는지 알 수 없기 때문입니다.이로 인해 개인 멤버를 설정하기 위해 모든 진입점 사용이 중단되는 프로그램이 다시 시작됩니다.

예외는 더 유용하지만 관리하기가 매우 무거울 수 있으며 예외를 너무 많이 사용할 가능성이 있습니다.그리고 추가 확인이 필요하며 코드를 최적화하는 것이 바람직하지 않을 수도 있습니다.개인적으로 나는 코드에 깊은 catch 제어가 필요할 때마다(catch 문이 호출 스택에서 매우 낮음) 또는 함수 매개 변수가 코드에 하드코딩되지 않을 때마다 예외를 사용합니다.

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