쓸모없는 예외 처리로 코드 강화


12

코드의 다른 부분이 올바르게 코딩되지 않은 경우를 대비하여 쓸모없는 예외 처리를 구현하는 것이 좋은 방법입니까?

기본 예

간단한 것이므로 모든 사람들을 느슨하게하지는 않습니다 :).

데이터베이스에서 추출되는 개인 정보 (이름, 주소 등)를 표시하는 앱을 작성한다고 가정 해 보겠습니다. 내가 UI 부분을 코딩하는 사람이고 다른 사람이 DB 쿼리 코드를 작성한다고 가정 해 봅시다.

이제 앱의 사양에 따라 개인 정보가 불완전한 경우 (즉, 데이터베이스에서 이름이 누락되었다고 가정) 쿼리를 코딩하는 사람이 누락 된 필드에 대해 "NA"를 반환하여이를 처리해야한다고 가정합니다.

쿼리가 잘못 코딩되어이 경우를 처리하지 않으면 어떻게됩니까? 쿼리를 작성한 사람이 불완전한 결과를 처리하고 정보를 표시하려고 할 때 코드가 빈 내용을 표시 할 준비가되지 않아 모든 것이 중단됩니다.

이 예는 매우 기본입니다. 나는 여러분 대부분이 "문제가 아니라이 충돌에 책임이 없다"고 말할 것이라고 믿습니다. 그러나 여전히 충돌하는 코드의 일부입니다.

또 다른 예

이제 쿼리를 작성하는 사람이라고 가정하겠습니다. 사양은 위와 동일하지 않지만 "삽입"쿼리를 작성하는 사람은 불완전한 정보를 삽입하지 않도록 데이터베이스에 사람을 추가 할 때 모든 필드가 완료되었는지 확인해야합니다. UI 사용자에게 완전한 정보를 제공하기 위해 "선택"쿼리를 보호해야합니까?

질문

사양에 명시 적으로 "이 사람은이 상황을 담당하는 사람입니다"라고 명시하지 않으면 어떻게됩니까? 제 3자가 다른 쿼리 (첫 번째 쿼리와 유사하지만 다른 DB)를 구현하고 UI 코드를 사용하여 표시하지만이 경우를 코드에서 처리하지 않으면 어떻게됩니까?

나쁜 경우를 처리해야하는 사람이 아니더라도 충돌 가능성을 방지하기 위해 필요한 조치를 취해야합니까?

나는 충돌을 해결할 책임이 없기 때문에 "충돌의 책임이있는 사람"과 같은 대답을 찾고 있지 않습니다. 알고 싶습니다. 내 책임이 아닌 상황에서 코드를 보호해야합니다. 다루다? 여기서는 "비어 있으면"간단한 것으로 충분합니다.

일반적으로이 질문은 중복 예외 처리에 대한 것입니다. 나는 프로젝트에서 혼자 일할 때 연속 함수에서 비슷한 예외 처리를 2-3 번 코딩 할 수 있기 때문에 묻습니다.


4
"테스트"에 대해 이야기하고 있지만 문제를 이해하는 한 "제작에 적용되는 테스트"를 의미하는 것이 "검증"또는 "예외 처리"라고합니다.
Doc Brown

1
예, 적절한 단어는 "예외 처리"입니다.
rdurand

다음 잘못된 태그를 변경
Doc Brown

당신은 참조 DailyWTF 당신은 - 확실히 당신이 테스트의이 종류의 일을하고 싶어?
gbjbaanb 12

@ gbjbaanb : 귀하의 링크를 올바르게 이해하면 그것이 내가 말하는 것이 아닙니다. 나는 "멍청한 테스트"에 대해 말하는 것이 아니라 예외 처리 복제에 대해 이야기하고 있습니다.
rdurand

답변:


14

여기서 말하는 것은 신뢰 경계 입니다. 응용 프로그램과 데이터베이스 사이의 경계를 믿습니까? 데이터베이스는 응용 프로그램의 데이터가 항상 사전 검증되었음을 신뢰합니까?

그것은 모든 응용 프로그램에서 내려야 할 결정이며 옳고 그른 대답은 없습니다. 나는 너무 많은 경계를 신뢰 경계라고 부르는 편에 잘못을 저지르는 경향이 있습니다. 다른 개발자들은 항상 당신이 기대하는 것을 항상 할 수 있도록 타사 API를 신뢰합니다.


5

견고성 원칙 "보내는 것에 보수적이며 받아들이 는 것에 자유 로워 라" 는 것이 당신이 따르는 것입니다. 좋은 원칙입니다-편집 : 응용 프로그램이 심각한 오류를 숨기지 않는 한-@ pdr에 동의하면 적용 여부에 따라 항상 상황에 따라 다릅니다.


어떤 사람들은 "견고성 원칙"이 쓰레기라고 생각합니다. 이 기사는 예를 제공합니다.

@ MatFenwick : 유효한 점을 지적 해 주셔서 감사합니다. 대답을 조금 변경했습니다.
Doc Brown

2
이것은 "견고성 원칙"의 문제점을 지적하는 훨씬 더 좋은 기사입니다 : joelonsoftware.com/items/2008/03/17.html
hakoja

1
@ hakoja : 솔직히, 나는이 기사를 잘 알고 있습니다 . 강건성 원칙을 따르지 않기 시작할 때 발생하는 문제에 관한 것입니다 (일부 MS 사람들은 최신 IE 버전으로 시도한 것처럼). 그럼에도 불구하고 이것은 원래의 질문에서 조금 멀어집니다.
Doc Brown

1
@DocBrown : 이것이 당신이 받아 들인 것에 대해 결코 자유 로워해서는 안되는 이유입니다. 견고 함은 불만없이 당신에게 던져진 모든 것을 받아 들일 필요가 없다는 것을 의미하지 않으며, 단지 충돌하지 않고 당신에게 던져진 모든 것을 받아 들여야한다는 것을 의미합니다.
Marjan Venema

1

테스트중인 대상에 따라 다릅니다. 그러나 테스트의 범위가 자신의 코드 일 뿐이라고 가정 해 봅시다. 이 경우 다음을 테스트해야합니다.

  • "행복한 사례": 응용 프로그램에 유효한 입력을 제공하고 올바른 출력을 생성하는지 확인하십시오.
  • 실패 사례 : 애플리케이션에 유효하지 않은 입력을 제공하고 올바르게 처리하는지 확인하십시오.

이렇게하려면 동료의 구성 요소를 사용할 수 없습니다. 대신 mocking을 사용 하십시오. 즉, 나머지 응용 프로그램을 테스트 프레임 워크에서 제어 할 수있는 "가짜"모듈로 바꿉니다. 이 작업을 정확히 수행하는 방법은 모듈의 인터페이스 방식에 따라 다릅니다. 하드 코딩 된 인수로 모듈의 메소드를 호출하는 것으로 충분할 수 있으며 다른 모듈의 공용 인터페이스를 테스트 환경과 연결하는 전체 프레임 워크를 작성하는 것처럼 복잡 할 수 있습니다.

그러나 그것은 단지 단위 테스트 사례입니다. 또한 모든 모듈을 함께 테스트하는 통합 테스트가 필요합니다. 다시 한 번, 행복한 사례와 실패를 모두 테스트하려고합니다.

"기본 예제"의 경우 코드를 단위 테스트하려면 데이터베이스 계층을 시뮬레이트하는 모의 클래스를 작성하십시오. 모의 클래스는 실제로 데이터베이스로 이동하지 않습니다. 예상 입력 및 고정 출력으로 미리로드하십시오. 의사 코드에서 :

function test_ValidUser() {
    // set up mocking and fixtures
    userid = 23;
    db = new MockDB();
    db.fixedResult = { firstName: "John", lastName: "Doe" };
    db.expectedCall = { method: 'getUser', params: { userid: userid } };
    userController = new UserController(db);
    expectedResult = "John Doe";

    // run the actual test
    actualResult = userController.displayUserAsString(userid);

    // check assertions
    assertEquals(expectedResult, actualResult);
    db.assertExpectedCall();
}

다음은 올바르게 보고 된 누락 된 필드를 테스트하는 방법입니다 .

function test_IncompleteUser() {
    // set up mocking and fixtures
    userid = 57;
    db = new MockDB();
    db.fixedResult = { firstName: "John", lastName: "NA" };
    db.expectedCall = { method: 'getUser', params: { userid: userid } };
    userController = new UserController(db);

    // let's say the user controller is specified to leave "NA" fields 
    // blank
    expectedResult = "John";

    // run the actual test
    actualResult = userController.displayUserAsString(userid);

    // check assertions
    assertEquals(expectedResult, actualResult);
    db.assertExpectedCall();
}

이제 상황이 재미있어집니다. 실제 DB 클래스가 제대로 작동하지 않으면 어떻게됩니까? 예를 들어, 불명확 한 이유로 예외가 발생할 수 있습니다. 우리는 그것이 있는지 알지 못하지만 자체 코드가 정상적으로 처리하기를 원합니다. 문제 없습니다. 예를 들어 다음과 같은 메소드를 추가하여 MockDB에서 예외를 발생시켜야합니다.

class MockDB {
    // ... snip
    function getUser(userid) {
        if (this.fixedException) {
            throw this.fixedException;
        }
        else {
            return this.fixedResult;
        }
    }
}

그런 다음 테스트 사례는 다음과 같습니다.

function test_MisbehavingUser() {
    // set up mocking and fixtures
    userid = 57;
    db = new MockDB();
    db.fixedException = new SQLException("You have an error in your SQL syntax");
    db.expectedCall = { method: 'getUser', params: { userid: userid } };
    userController = new UserController(db);

    // run the actual test
    try {
        userController.displayUserAsString(userid);
    }
    catch (DatabaseException ex) {
        // This is good: our userController has caught the raw exception
        // from the database layer and wrapped it in a DatabaseException.
        return TEST_PASSED;
    }
    catch (Exception ex) {
        // This is not good: we have an exception, but it's the wrong kind.
        testLog.log("Found the wrong exception: " + ex);
        return TEST_FAILED;
    }
    // This is bad, too: either our mocking class didn't throw even when it
    // should have, or our userController swallowed the exception and
    // discarded it
    testLog.log("Expected an exception to be thrown, but nothing happened.");
    return TEST_FAILED;
}

이것들은 단위 테스트입니다. 통합 테스트의 경우 MockDB 클래스를 사용하지 않습니다. 대신 실제 클래스를 모두 연결합니다. 여전히 비품이 필요합니다. 예를 들어 테스트를 실행하기 전에 테스트 데이터베이스를 알려진 상태로 초기화해야합니다.

이제 책임이있는 한 : 코드는 나머지 코드베이스가 사양에 따라 구현 될 것으로 예상해야하지만 나머지 코드가 고정 될 때도 정상적으로 처리 할 수 ​​있도록 준비해야합니다. 귀하는 자신의 코드가 아닌 다른 코드를 테스트 할 책임 없지만, 코드의 반대쪽에있는 코드를 복원력있게 만드는 책임은 물론 코드의 탄력성을 테스트 할 책임도 있습니다. 그것이 위의 세 번째 테스트가하는 것입니다.


질문 아래의 의견을 읽었습니까? OP는 "테스트"를 작성했지만 "유효성 검사"및 / 또는 "예외 처리"라는 의미에서이를 의미했습니다
Doc Brown

1
@ tdammers : 오해에 대해 유감스럽게 생각합니다. 사실 예외 처리를 의미했습니다. 어쨌든 완전한 대답에 감사드립니다. 마지막 단락은 내가 찾고있는 것입니다.
rdurand 12

1

내가 코딩하려고하는 3 가지 주요 원칙이 있습니다.

  • 마른

  • 키스

  • 야 그니

이 모든 것들은 다른 곳에서 복제 된 유효성 검사 코드를 작성할 위험이 있다는 것입니다. 유효성 검사 규칙이 변경되면 여러 위치에서 업데이트해야합니다.

물론, 미래의 어느 시점에서, 데이터베이스를 리 플랫폼으로 만들 있으며 (이 경우), 둘 이상의 위치에 코드를 갖는 것이 유리하다고 생각할 수 있습니다. 그러나 ... 당신은 일어날 수없는 것을 코딩하고 있습니다.

추가 코드 (변경되지 않더라도)는 작성, 읽기, 저장 및 테스트가 필요하므로 오버 헤드입니다.

위의 모든 사항이 사실이므로 유효성 검사를 전혀하지 않는 것이 좋습니다. 응용 프로그램에서 전체 이름을 표시하려면 데이터 자체의 유효성을 검사하지 않더라도 몇 가지 기본 데이터가 필요합니다.


1

평신도의 말로.

"데이터베이스" 또는 "응용 프로그램" 과 같은 것은 없습니다 .

  1. 데이터베이스는 둘 이상의 응용 프로그램에서 사용할 수 있습니다.
  2. 응용 프로그램은 둘 이상의 데이터베이스를 사용할 수 있습니다.
  3. 데이터베이스 정의는 기본값이 테이블 정의에 정의되어 있지 않으면 필수 필드가 삽입 조작에 포함되지 않은 경우 오류 발생을 포함하여 데이터 무결성을 강제해야합니다. 앱을 무시하고 행을 데이터베이스에 직접 삽입하더라도이 작업을 수행해야합니다. 데이터베이스 시스템에서이를 수행하십시오.
  4. 데이터베이스는 데이터 무결성을 보호하고 오류를 발생 시켜야 합니다 .
  5. 비즈니스 로직은 이러한 오류 포착 하고 프리젠 테이션 계층에 예외 를 발생 시켜야합니다 .
  6. 프리젠 테이션 레이어는 입력을 확인 하고 예외를 처리 하거나 사용자에게 슬픈 햄스터를 보여 주어야합니다.

다시:

  • 데이터베이스-> 던지기 오류
  • 비즈니스 로직-> 캐치 오류 및 예외 발생
  • 프리젠 테이션 레이어-> 확인, 예외 발생 또는 슬픈 메시지 표시
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.