본문 바로가기

.Net Technology/.NET TDD

(16) TDD를 위한 객체지향 - 실전! 솔리드 원칙들의 도입 지금까지 살펴본 내용을 실제 예제를 통해서 실전 감각을 익혀보도록 하겠다. 먼저 아래와 같은 프로그램을 설계하고 개발해야 된다고 가정해보자. 편의점 카운터에서 물건을 스캔한 뒤에 고객에게 최종 가격을 알려주는 프로그램이 필요합니다. 물건들의 가격은 데이터 베이스에 있으며, 한가지 특별한 것은 현재 모든 물건을 2개 사면 하나 더 주는 이벤트를 하고 있기 때문에 이 로직을 반영해주세요. 위의 요청 사항을 보다 간단히 생각하고 구현해 보도록 하겠다. 우리는 하나의 메서드를 제공해 줄 것이다. 즉, 실행 프로그램은 List배열로 이루어진 각 물건 코드들의 리스트를 전달하면 각 물건의 가격들을 합산한 가격을 알 수 있으면 되는 것이다. 단, 같은 코드가 3개가 되면 하나의 물건 가격은 제외되어야 한다. 먼저 필.. 더보기
(15) TDD를 위한 객체지향 - 의존성의 반전 (Dependency inversion) 마지막으로 원칙이 무엇보다도 TDD에서 가장 중요한 부분이다. 다른 원칙들이 지켜지지 않는다고 하더라도 TDD를 진행하는데 불편함이 있을 뿐이지 그렇게 어렵지는 않다. 하지만 만약 의존성 반전이라는 원칙이 지켜지지 않는다면 TDD 자체가 어려워진다. 의존성 반전이라는 것은 쉽게 이야기 해서 객체간의 강한 참조를 없애는 것이다. 즉, 느슨한 결합을 인터페이스를 통해서 실현하는 것을 의미한다. 그럼 첫번째 원칙에서 살펴본 코드의 문제를 살펴보도록 하자. class FileLogger { public void Handle(string error) { System.IO.File.WriteAllText(@"c:\Error.txt", error); } } class Customer { private FileLogg.. 더보기
(14) TDD를 위한 객체지향 - 인터페이스의 분리 (Interface segregation) 인터페이스를 이용해서 객체를 기능별로 잘 분리해야 한다는 원칙이다. 솔리드의 첫번째 규칙이었던 객체는 하나의 기능을 가지고 있어야 한다는 것과 의미적으로 보면 같은 맥락이 될 수 있다. 하지만 여기서의 의미는 인터페이스를 통해서 기능을 세부저긍로 분리하는 것이다. 바로 앞의 예제가 완벽하지 않은 이유는 일반 고객은 할인이 불가능함에도 불구하고 GetDiscount라는 메서드를 가지고 있기 때문이다. 필자는 이 기능을 분리하기 위해서 아래와 같이 인터페이스를 추가해보도록 하겠다. public interface IDiscount { double GetDiscount(double totalSales); } 이제 위의 소스를 아래와 같이 변경해보도록 하자. public interface IDiscount { d.. 더보기
(13) TDD를 위한 객체지향 - 리스코브 대입 (Liskov substitution) 리스코브 대입의 규칙은 이러하다. “프로그램에 존재하는 한 객체는 어떤 추가적인 수정없이 파생된 서브 클래스들로 대입이 가능해야 한다.” 다시 풀어 설명하자면 자식 클래스들이 부모의 기능을 수행하는데 있어서 어떤 문제가 있어서는 안된다는 것이다. 쉽게 이해하기 위해서 바로 앞에서 사용한 예제를 통해서 그 문제와 해결방안에 대해서 자세히 살펴보도록 하자. 먼저 새로운 기능을 추가해보도록 하겠다. 바로 실버와 골드 고객에게만 스페셜 서비스를 주고 싶은 것이다. 즉, 스페셜 서비스를 요청하면 이 요청을 받아서 DB에 저장하는 메서드를 추가하는 것이다. 먼저 실버는 고객 클래스를 골드는 실버 클래스를 상속받고 있는 구조이기 때문에 우리는 부모인 Customer에 SetSpecialService라는 가상 메서드를.. 더보기
(12) TDD를 위한 객체지향 - 개방과 폐쇄 (Open-closed) 개방과 폐쇄라고 해서 단순히 private나 public을 적절히 이용해야 된다고 생각하면 큰 오산이다. 이 개방과 폐쇄의 원칙은 확장에 있어서는 오픈하고 수정에 있어서는 폐쇄하라는 의미이다. 각각의 유닛들이 만들어 질때virtual 메서드를 적절히 이용하거나 상속을 이용할 수 있으면 우리는 보다 객체지향적인 코드를 생성할 수 있다. 예를 들어 보도록 하겠다. 만약 호텔의 고객을 관리한다고 가정할때 고객의 등급이 일반, 실버, 골드로 나누어 진다고 가정해보자. 그리고 각각의 고객별로 다르게 할인율이 적용된다고 가정하겠다. 이때 이 할인율을 계산하는 GetDiscount라는 메서드를 만들어야 한다면 어떻게 클래스를 설계할지 생각해보자. 일반적으로 다형성의 개념을 도입하지 않는다면 아래와 같은 코드가 작성될.. 더보기
(11) TDD를 위한 객체지향 - 하나의 기능 (Single Responsibility) 첫번째 디자인 규칙은 객체에서 수많은 기능이 아닌 단, 하나의 기능만 포함시키는 것이다. 쉽게 그림으로 이해해보도록 하자. 만약 클래스가 [그림1]처럼 만들어졌다면 어떨까? [그림1] 맥가이버 칼 이 도구를 사용하는데 있어서 큰 문제는 먼저 어디에 어떤 도구(기능)가 있는지 찾기 어렵다는 것이다. 필자 또한 필자의 클래스를 돌아보면 수많은 기능들이 포함되어지는 경우가 많다. 예를 들어 게시판의 글을 가져옴과 동시에 사용자의 정보들도 가져온다고 생각해보자. 이때 만약 게시글의 리파지토리와 사용자 리파지토리에서 각각의 정보를 가지고 올 수 있다면 위의 역할을 충족시키지만 하나의 클래스에서 작성되어진다면 이 원칙을 벗어나게 된다. TDD에서 유닛의 개념과 이 원칙의 개념은 상당히 비슷하다. 아주 심플한 예제로.. 더보기
(10) TDD를 위한 객체지향 - 솔리드(SOLID) 디자인 원칙 이번 장에서는 TDD를 하기 위해서 필요한 객체지향의 개념에 대해서 살펴본다. 객체지향이라고 해서 클래스가 무엇인지 인터페이스가 무엇인지를 다룬다기 보다는 이러한 개념을 가지고는 있지만 이것을 제대로 활용하기 위한 내용이 될 것이다. TDD를 도입하면서 가지게 되는 이점 중 하나가 바로 객체지향적인 코드를 보장해 주는 것이라고 언급했었다. 물론, TDD를 하면서 설계에 많은 고민을 가지게 됨으로써 보다 객체지향적인 코드를 설계하고 배워 나가는 것도 있다. 하지만이번 장에서는 그러한 시행착오를 없애기 위한내용으로 TDD를 위한 필수적인 객체지향 패턴들과 규칙들을 살펴보도록 하겠다. 솔리드 디자인 원칙 솔리드(SOLID)는 다섯개의 객체지향 패턴으로 구성된 디자인 컨셉이다. 솔리드의 의미는 실제 그 각각의 .. 더보기
(9) NUnit 시작하기 - NUnit의 실전 활용 2부 다른 객체와의 결합 지금까지의 예제는 NUnit을 사용해볼 정도의 아주 간단한 상황을 가정하였다. 우리는 지금의 지식으로는 TDD를 실무에 적용하기에 무리가 있다. 먼저 통합테스트가 아닌 유닛 테스트를 위해서 “컨트롤의 반전”이라는 IOC(Inversion of Control)의 개념을 이해하고 도입해야 하기 때문이다. 이 개념을 이해하기 위해서 왜 TDD에서 이 지식이 필수로 필요한지에 대해서 앞의 예제를 조금 수정함으로 알아보도록 하자. 먼저 앞에서 살펴본 세금예제에서 아래와 같은 수정사항이 있다고 가정하자. - 세금 비율을 DB에 저장해서 연도 별로 가져오도록 수정해주세요. 충분히 이해할 수 있는 요청 사항일 것이다. 먼저 DB와 연동하기 위해서 가장 대중적인 패턴인 Repository를 생성해 보.. 더보기
(8) NUnit 시작하기 - NUnit의 실전 활용 1부 지금 살펴본 NUnit을 사용해보는 것을 목적으로 TDD 기반의 애플리케이션을 만들어 보도록 하겠다. 먼저 만들고자 하는 프로그램은 아래와 같다. 연봉에서 현재 나라에 내야 하는 Tax를 뺀 연봉의 실 수령액을 계산해서 반환해주는 프로그램을 만드시오. 단, Tax 계산 년도가 2013년일때는 10%를 2014년일때는 20%를 적용하시오. 위의 문제는 간단하게 연봉을 전달해주면 거기서 Tax를 제외한 실수령 액을 계산해주면 되는 것이다. 처음 1장에서 설명했던 TDD의 개발 프로세스를 생각해보자. 먼저 우리는 프로그램 골격을 먼저 만든 후에 테스트 코드를 작성한다. 이 때 우리는 모든 가능한 테스트 요소들을 생각해서 각 케이스 별로 테스트를 만들어야 한다. 그렇게 테스트 코드가 작성되었으면 우리는 실전 코.. 더보기
(7) NUnit 시작하기 - Assert 클래스의 기능 대부분의 테스트 메서드에서는 예상 된 결과를 비교 하면서 테스트 결과를 테스트 프레임워크에 전달하게 된다. 이 때 이 역할을 수행해주는 것이 NUnit의 Assert 클래스이다. 지금까지의 테스트 예제에서는 Assert의 AreEqual이라는 메서드를 이용해서 두 값을 비교하면서 테스트 결과를 살펴봤었다. 하지만 AreEqual이나 AreSame과 같은 메서드는 실제 지금도 지원을 하고 있지만 오래된 문법이다. 실제 NUnit이 2.4로 버전업을 하면서 새로운 문법을 소개했었는데 그것은 바로 That이라는 문법이다. 그럼 상황에 따라서 어떻게 값을 비교할 수 있을지 살펴보도록 하자. 팁! 값 타입과 참조타입의 == 키워드의 이용 알다시피 닷넷에서는 참조 타입과 값 타입 이렇게 두 개의 타입이 존재한다. .. 더보기
(6) NUnit 시작하기 - NUnit의 애트리뷰트들 NUnit에서 지원하는 애트리뷰트들에 대해서 먼저 살펴보도록 하자. TestFixture 바로 앞의 예제에서는 클래스 위에 TestFixture를 이용해서 테스트를 정의 했다. 이 애트리뷰트는 주로 테스트 메서드들을 가지고 있음을 명시해주기 위해서 사용한다. 다시 말해서, 만약 이 애트리뷰트를 클래스에 정의하게 되면 내부적인 테스트 애플리케이션이 내부적인 테스트 메서드들이 존재하는지 스캔하게 된다. 아래 코드는 이 테스트 메서드를 사용하는 방법을 보여준다. using System; using NUnit.Framework; namespace UnitTestingExamples { [TestFixture] public class SomeTests { } } Test Test 애트리뷰트는 TestFixture.. 더보기
(5) NUnit 시작하기 - 환경설정 대부분의 테스트 프레임워크들은 다음과 같은 순서들을 제공한다. 1. 테스트 코드 작성2. 테스트 실행3. 결과 확인4. (옵션) 테스트 코드 수정 먼저 테스트 코드를 작성하기 전에 개발환경을 셋팅해야 한다. MSTest는 마이크로소프트에서 제공하는 기본 테스트 툴인데 이 툴을 이용할 경우에는 별다른 셋업이 필요 없지만 NUnit을 비주얼 스튜디오 테스트 창과 연동하기 위해서는 아래의 주소에서 NUnit 테스트 아답터라고 불리는 통합 UI 도구를 다운 받아서 설치해도 되고 Nuget 페키지에서 직접 설치해도 된다. 다시 정리하자면 NUnit이라는 라이브러리와 TestAdapter라는 두개의 프레임워크가 필요하다. - NUnit 테스트 아답터(NUnit Test Adapter)http://visualstud.. 더보기
(4) NUnit 시작하기 - 유닛테스트와 프레임워크 앞에서 이미 통합 테스트(Integrate Test)와 유닛 테스트(Unit Test)의 차이가 무엇인지 그리고 왜 유닛 테스트가 중요한지에 대해서 설명했다. 이번 장에서는 보다 자세히 유닛 테스팅이 무엇인지 살펴보고 또한 NUnit을 이용해서 테스트 코드를 작성하는 방법을 살펴보도록 하겠다. 유닛 테스트의 정의 먼저 유닛 테스트를 간단하게 정의하자면 하나의 프로그램 유닛을 테스트 할 수 있는 단위로 정의 할 수 있다. 여기서 하나의 프로그램 유닛이라는 것은 하나의 기능을 가지고 있는 메서드 정도가 될 수 있다. 앞에서 1부터 20까지의 수 중에서 3의 배수와 5의 배수일 때 특정 문자열을 반환하는 예제를 살펴봤었다. 여기서 우리는 for문 안에서 몇 번을 반복하든지 크게 상관하지 않았다. 중요한 것은 .. 더보기
(3) TDD의 소개 - 예제로 살펴보는 TDD 개발 프로세스 TDD 개발 프로세스 먼저 아래와 같은 프로그램을 작성한다고 가정하겠다. 1부터 20까지의 수를 출력하는 프로그램을 작성하시오. 단, 3의 배수에서는 숫자대신 “JOEL”를 5의 배수에서는 “SARAH” 그리고 3과 5의 배수에서는 “JOEL SARAH” 를 출력하시오. 위의 프로그램을 Console에 작성한다고 가정할 때 먼저 지금까지 개발해온 개발 방법론으로 접근해보도록 하겠다. 아마도 80% 이상의 개발자들은 아마 아래와 같은 코드를 작성할 것이다. static void Main(string[] args) { for (int i =1 ; i 더보기
(2) TDD의 소개 - 통합테스트와 유닛테스트 먼저 TDD에서 도입하는 유닛 테스트를 이해하기 위해서 기존의 테스트 프로세스를 먼저 살펴볼 필요가 있다. 대부분의 프로젝트에서는 통합 테스트(Integration Test)를 통하여 소프트웨어를 테스트하고 진단하게 된다. 먼저, 우리가 운전하고 있는 자동차가 어떤 문제가 생기면 어떻게 될까? 혼자서 문제를 진단하고 알아 내는 것이 과연 가능할까? 자동차의 엔진들은 보통 많은 부품들과 함께 작동되기 때문에 어떤 것들이 어떻게 연결되어 있는지 알아야만 진단이 가능하다. 아쉽게도 소프트웨어도 자동차의 엔진과 굉장히 비슷하다. 많은 엔진들과 기능들이 서로 모듈 별로 연결되어 있기 때문에 어떤 한 모듈이라도 제대로 동작되지 않으면 전체 기능이 동작되지 않게 되는 경우가 많다. 아래 [그림3]의 여러 층으로 설계.. 더보기