본문 바로가기

C#

[C#] Code Contracts 사용하기

Code Contracts는 이전 방식인 assert들을 깔끔하게 정리할 수 있는 새로운 방법입니다.

Code Contracts를 사용하기 위해서는 먼저 Visual Studio 확장 프로그램을 다운받아야 합니다.
https://visualstudiogallery.msdn.microsoft.com/1ec7db13-3363-46c9-851f-1ce455f66970

위 링크에서 프로그램을 다운받고 설치 후, Visual Studio를 재시작합니다.


Code Contracts를 테스트하기 위한 테스트 솔루션/프로젝트를 만듭니다. 여기서는 C#을 사용하겠습니다.


프로젝트의 Properties에 들어가보면 탭 아래에 Code Contracts 탭이 새로 추가된것을 볼 수 있습니다. 해당 탭에 들어가서 아래 그림과 같이 설정합니다.

  • Assembly Mode : Standard Contract Requires를 선택합니다.
  • Runtime Checking : 체크박스에 체크합니다


이제 설정을 완료했으니, 테스트 코드를 작성합니다.
Program.cs를 열고 아래와 같이 작성합니다.

Foo라는 클래스를 만들고 PositiveSum 메소드를 만들었습니다. 이 메소들은 두 양수간의 합을 구하는 메소드입니다.
하지만 지금 단계에서는 값이 실제로 양수가 넘어오는지, 반환값도 실제로 양수가 반환되는지 체크하는 로직이 전혀 없습니다.

이제 이 PositiveSum 메소드에 Code Contracts를 이용해서 검사하는 코드를 추가해보겠습니다.

Contract.Requires 메소드를 이용해 넘겨받은 파라미터들을 검사합니다. Requires는 메소드가 호출된 시점에 실행되며, 넘겨받은 조건식이 true인지 검사합니다. 만약 true가 아닐경우 익셉션을 발생시킵니다.

한번 Foo.PositiveSum을 호출할 때 인자를 음수로 넣어 익셉션을 발생시켜 보겠습니다.

익셉션이 발생해서 값이 잘못되었음을 알려줄 수는 있지만, 익셉션을 받는 쪽에서 보기에는 그리 친절한 익셉션은 아닙니다. 조건식이 실패했을 때 던지는 익셉션을 ArgumentOutOfRangeException으로 바꾸어 보겠습니다.


이렇게 조건식이 실패했을 때 던질 익셉션을 지정하면, 해당 익셉션을 발생시킵니다. 익셉션을 받는 쪽에서 처리하기 쉽도록 명확한 익셉션을 지정하여 검사하는것이 중요합니다.

하지만, 앞으로 이 글의 예제에서 모든 Contract에 대해서 일일히 익셉션의 이름을 지정하는것은 생략하도록 하겠습니다.


Requires를 사용해서 넘어오는 인자를 검사했지만, 메소드의 동작 자체를 검증해야 하는 경우도 존재합니다. 이러한 경우를 위해 Code Contracts에서는 메소드 실행이 끝난 후에 동작하는 검증 로직을 제작할 수 있는 방법도 제공합니다.

PositiveSum메소드의 Requires 아래에 다음의 코드를 추가합니다.


양수 두 값을 더한 결과값은 당연히 양수여야 합니다. Contract.Ensure 메소드는 메소드 종료 이후에 조건식이 실행됨을 지정하고, Contract.Result는 메소드의 반환값을 가져옵니다. 결과적으로 위 코드는 메소드의 반환값이 양수임을 검증하는 코드입니다.

현재의 코드는 매우 간단해서 실수의 여지가 거의 없지만, 메소드가 복잡해지거나 가끔가다 실수로 오타를 작성하는 경우가 있습니다. 여기서는 그러한 경우를 가정하고 PositiveSum 메소드가 두 수룰 더하는것이 아니라 뺀 값을 반환하도록 수정해보겠습니다.


위의 PositiveSum 메소드를 Foo.PositiveSum(1, 30); 등과 같은 경우로 실행하면 아래와 같은 익셉션이 발생하는것을 볼 수 있습니다.

처리되지 않은 예외: System.Diagnostics.Contracts.__ContractsRuntime+ContractException: 사후 조건 실패: Contract.Result<int>() > 0


지금까지 Requires, Ensures를 사용하여 메소드 안에서의 데이터 검증 기능을 테스트해보았습니다. 하지만 Code Contracts는 메소드 스코프를 넘어서 인스턴스 레벨에서의 검증 기능도 제공합니다.

이번에는 Person이란 클래스를 제작합니다.

(name과 age는 getter만 제공하는것이 좋겠지만, 테스트 용도로 사용해야 하니 setter도 공개합니다.)

메소드 중 Check라는 메소드가 있습니다. 이 메소드는 객체가 유효한 상태인지 검사하는 역할을 합니다. 여기서는 name은 null이나 empty일 수 없고, age는 음수일수 없다는 조건을 지정하였습니다.

이렇게 유효성을 검사하는 메소드는 ContractInvariantMethod속성을 가지고 있어야 하고, 반드시 private로 설정되어야 합니다.

Person 클래스에 고의적으로 잘못된 값을 넣어 작성한 유효성 검사기가 제대로 동작하는지 테스트해보겠습니다.

객체를 생성할때는 올바른 값을 넣어 생성했지만, 후에 age에 음수값을 넣어 잘못된 값을 입력하고 있습니다.

이 코드를 실행하면 다음과 같은 익셉션이 발생합니다.

처리되지 않은 예외: System.Diagnostics.Contracts.__ContractsRuntime+ContractException: 사전 조건 실패: age >= 0


지금까지 Code Contracts 기능을 이용한 사전, 사후 처리 / 인스턴스의 유효성을 검사하는방법을 알아보았습니다. 이러한 기능을 잘 활용하면 실수를 줄일 수 있고, 무엇보다도 작성한 코드를 사용하는 사용자에게 용법상의 실수를 바로바로 알려줄 수 있어 유용합니다.



'C#' 카테고리의 다른 글

[Nancy] 에러 핸들링, 핸들러 등록하기  (0) 2016.01.29
[C#] NUnitLite, Jenkins CI 연동하기  (0) 2016.01.04
[C#] NUnit, NUnitLite 사용하기  (0) 2015.12.10
[C#] string과 String의 차이  (0) 2015.11.11
[C#] Using static 사용하기  (0) 2015.10.21