Visual Studio 2005 Team System을 사용하여 단위 테스트 수행 및 단위 테스트 프레임워크를 위한 소스 코드 생성
Scott Dockendorf
Telligent Systems, Inc.
2005년 9월
적용 대상:
Microsoft Visual Studio 2005 Team System 베타 2
Team Architect & Team Test Editions
Microsoft Visual C# 2005
요약: 이 문서에서는 자동화된 단위 테스트의 기초적인 내용과 Microsoft Visual Studio 2005 Team System에서 제공되는 단위 테스트 프레임워크에 들어 있는 코드 생성 엔진에 대해 자세히 설명합니다.
목차
소개
단위 테스트에 대한 재고찰
자동화된 단위 테스트 정보
왜 코드를 생성합니까?
코드를 몇 개 생성해 봅시다!
무엇이 생성되었습니까?
생성 후에는 이제 무엇을 해야 합니까?
단위 테스트 코드 재생성
자동화된 단위 테스트 권장 사항
결론
소개
혁신과 발전으로 기업이 성장하면 기업을 운영하는 시스템의 규모도 함께 커집니다. 기업이 발전하여 파트너, 고객 및 공급업체와 통합될수록 이러한 시스템은 점점 더 복잡해질 것입니다.
이러한 복잡한 상황 속에서 IT 책임자는 개발 프로세스 도중에(즉, 구현 이전 단계에서) 품질을 보장해야 합니다. 품질을 보장하기 위한 한 가지 방법은 사용자 지정 코드에 대한 자동화된 단위 테스트를 엄격하게 실행하여 QA 주기에서 발견되는 결함의 수를 개발 직원이 줄일 수 있게 하는 것입니다. 개발 주기에서 자동화된 단위 테스트를 적용하면 사용자 지정 코드를 사용하는 방법에 대한 간편하고 문서화된 예제가 팀 구성원에게 제공됩니다.
구조화 및 자동화된 단위 테스트를 사용하는 데 있어서 한 가지 문제가 되는 것은 이러한 작업을 수행하는 데 필요한 코드가 매우 많다는 점입니다. 간단하게 정의해서 “소프트웨어를 만드는 소프트웨어”라고 볼 수 있는 코드 생성의 개념은 회사의 IT 개발 부서에서 점점 일반화되고 있습니다. 어떤 사람들은 코드 생성이 “출시 시간” 전략을 줄이는 데 도움이 되고 내부 표준/규칙을 적용하는 데 도움이 되며 개발 프로세스를 향상시킬 것이라 믿고 있습니다.
이러한 요구 사항을 감안하여 Microsoft는 풍부한 기능의 코드 생성 엔진을 차세대 개발 도구인 VSTS(Visual Studio 2005 Team System)에 포함했습니다. 이 문서에서는 단위 테스트 코드 생성에 대한 단계별 가이드와 정기적으로 단위 테스트 코드 생성을 사용할 수 있는 방법에 대한 자세한 정보를 제공합니다.
단위 테스트에 대한 재고찰
여러분이 대규모 개발 팀의 구성원이며 회사의 차세대 시스템을 작성해야 한다고 가정해 보십시오. 여러분은 가능한 한 많은 Microsoft ASP.NET/Microsoft WinForms를 꾸준히 만들어야 하는 UI 개발자입니다. UI 개발자는 데이터베이스 CRUD(Create-Retrieve-Update-Delete) 뿐만 아니라 시스템의 전체 엔티티와 연관된 비즈니스 규칙을 수행하도록 설계된 중간 계층 구성 요소를 완성하기 위해 “중간 계층” 팀에 의존합니다.
UI 개발을 시작한지 몇 주 후에 폼이 완성되었으며 중간 계층 개발자가 클래스 라이브러리를 UI 개발자에게 제공할 준비가 되었습니다. 표 1은 이러한 과정에서 흔히 주고 받을 수 있는 대화 내용을 보여 줍니다.
표 1. UI 개발자와 중간 계층 개발자 사이에 주고 받는 대화
중간 계층: | “이제 개체를 사용할 준비가 되었습니다. 최신 버전의 OurSystemBL.dll을 사용하면 다 잘 될 것입니다.” |
UI: | “감사합니다. 저희가 검토할 수 있는 어떤 문서가 있습니까?” |
중간 계층: | “예, 있습니다. 코딩을 수행할 수 있는 시간이 충분했습니다. 디자인 문서를 확인하시기 바랍니다. 아, 잠시만요. 아직까지도 완성되지 않았네요. 조만간 곧 완성될 것입니다.” |
UI: | “XML 문서를 사용하셨나요?” |
중간 계층: | “생성자에서 사용되었지만 메서드에서는 그리 많이 사용되지 않았습니다.” |
UI: | “개체를 작성, 실행 및 분리하는 방법을 보여 주는 샘플 코드가 있습니다?” |
중간 계층: | “필요한 모든 것을 제공하는 샘플 WinForms 응용 프로그램을 첨부했습니다. 하지만 이 응용 프로그램은 제 워크스테이션에 있던 것이라 Microsoft Visual SourceSafe에는 없습니다.” |
UI 개발자는 이러한 재미 있는 프로젝트를 적용하는 방법을 궁리하면서 여러 가지 사항을 고려하다가 중간 계층의 단위 테스트 집합을 검토하기로 결정합니다. 코드를 구체적으로 살펴본 후에 UI 개발자는 레이블 지정되지 않은 두 개의 텍스트 상자와 button1, button2 및 button3이라는 레이블이 지정된 세 개의 단추가 폼에 있다는 것을 알게 됩니다. (운이 좋다면 이러한 요소가 폼에서 정렬되어 있을 것입니다.) 그런 다음 이러한 단추와 연관된 이벤트를 조사하면 주석 처리된 코드가 하나도 없다는 것과 데이터 변수의 이름이 x, y 및 z라는 것을 알 수 있습니다. 다행스럽게도 button1 및 button2가 개체의 Save() 메서드를 수행하고 button3이 Delete() 메서드를 수행한다는 것을 알게 되었습니다. 테스트를 실행하면 다양한 구성 설정이 누락되어 있기 때문에 많은 System.Exception 오류가 발생합니다.
위의 내용은 아주 극단적인 경우이므로 실제로는 이러한 상황이 발생해서는 안 될 것입니다. 하지만 이러한 상황에 존재하는 “단위 테스트”와 관련된 문제를 살펴보도록 하겠습니다.
- 이 형태의 단위 테스트 코드는 구조적이지 않습니다. 코드는 단추 클릭 이벤트로 발생하며 해독하기 어렵습니다.
- 이 형태의 단위 테스트 코드는 제대로 문서화되지 않았습니다.
- 이 형태의 단위 테스트는 적절하거나 부적절하다고 알려진 데이터에 기반을 두지 않습니다. 모든 것은 레이블 지정되지 않은 텍스트 상자에 입력되는 내용에 달려 있습니다.
- 단위 테스트 코드를 입력된 코드에 기초하여 자동으로 반복할 수 없습니다.
- 실제로 테스트된 코드의 양을 나타내는 데이터인 단위 테스트 코드 적용 범위를 알 수 없습니다.
- 팀 구성원 간에 구현 세부 정보를 쉽게 주고 받을 수 없습니다.
자동화된 단위 테스트 정보
xUnit 프레임워크는 1998년에 eXtreme Programming의 핵심 개념으로 도입되었습니다. xUnit 프레임워크는 구조화 및 자동화된 효율적 단위 테스트를 일반 개발 작업에 추가할 수 있는 효율적 메커니즘을 개발자에게 제공합니다. 그 이후로 이 프레임워크는 자동화된 단위 테스트 프레임워크를 위한 사실상의 표준으로 발전했습니다.
자동화된 단위 테스트의 이점
간단하게 말해서 자동화된 데스크는 다음과 같은 이점이 있습니다.
- 구조적입니다.
- 문서화되어 있습니다.
- 자동으로 수행되며 반복 가능합니다.
- 알려진 데이터에 기반을 둡니다.
- 긍정적 및 부정적 작업을 테스트하도록 설계되었습니다.
- 여러 다른 시스템에서의 구현 테스트에 이상적입니다.
- 구성, 구현 및 실행 예제를 제공합니다.
xUnit 프레임워크 요소
표 2에는 xUnit 프레임워크의 기본 개념과 이에 해당하는 Visual Studio 2005 Team System 단위 테스트 프레임워크의 개념이 나와 있습니다.
표 2. xUnit 프레임워크 개념과 VSTS 단위 테스트 프레임워크 개념
xUnit 프레임워크 개념 | VS 2005의 해당 개념(아래 특성 참조) | 설명 |
---|---|---|
테스트 | TestMethod | 간단하게 말해서 테스트 자체입니다. 예상된 결과를 테스트하고 결과가 달성되지 않은 경우를 보고하는 논리입니다. 이 개념을 “메서드”라고 생각할 수 있습니다. |
테스트 기능 | TestClass | 일대다 테스트의 논리적 그룹입니다. 이 개념을 “클래스”라고 생각할 수 있습니다. |
테스트 모음 | Test List ** | 일대다 테스트 기능의 논리적 그룹입니다. 이 개념을 “클래스 라이브러리”로 생각할 수 있습니다. 참고이 개념에는 특성이 필요하지 않습니다. |
테스트 실행기 | VS 2005 VSTS 단위 테스트 프레임워크 | 테스트 결과의 검색, 실행 및 보고를 담당하는 GUI/콘솔 응용 프로그램입니다. 이 문서에서는 Visual Studio 2005 Team System이 테스트 실행기의 역할을 수행합니다. |
테스트 기능 예제
다음은 BankAccount 클래스에 대한 클래스 다이어그램과 샘플 테스트 기능(BankAccountTests.cs)입니다.
그림 1. BankAccount 클래스
샘플 테스트 기능: BankAccountTests.cs
using BankAccountDemo.Business;using Microsoft.VisualStudio.QualityTools.UnitTesting.Framework;
namespace BankAccountDemo.Business.Tests
{
[TestClass()]
public class BankAccountTest
{
[TestInitialize()]
public void Initialize() {
}
[TestCleanup()]
public void Cleanup() {
}
[TestMethod()]
public void ConstructorTest() {
float currentBalance = 500;
BankAccount target = new BankAccount(currentBalance);
Assert.AreEqual(currentBalance, target.CurrentBalance,
"Balances are not equal upon creation");
}
[TestMethod()]
public void DepositMoneyTest() {
float currentBalance = 500;
BankAccount target = new BankAccount(currentBalance);
float depositAmount = 10;
target.DepositMoney(depositAmount);
Assert.IsTrue( (currentBalance + depositAmount) >
target.CurrentBalance,
"Deposit not applied correctly");
}
[TestMethod()]
public void MakePaymentTest() {
float currentBalance = 500;
BankAccount target = new BankAccount(currentBalance);
float paymentAmount = 250;
target.MakePayment(paymentAmount);
Assert.IsTrue(currentBalance - paymentAmount ==
target.CurrentBalance,
"Payment not applied correctly");
}
}
}
기본 단위 테스트 개념 == 어설션
이 형태의 단위 테스트에 사용된 기본 개념은 자동화된 단위 테스트가 “진실 또는 진실이라고 믿는 무엇”으로 정의될 수 있는 “어설션”에 기반을 둔다는 것입니다. 논리적인 관점에서 보면 이러한 어설션은 “{x}를 수행할 경우의 예상 결과는 {y}입니다.”라고 말할 수 있습니다.
Microsoft.VisualStudio.QualityTools.UnitTesting.Framework 네임스페이스에서 제공되는 세 가지 “어설션” 클래스인 Assert, StringAssert 및 CollectionAssert를 사용하여 이러한 개념을 코드에 매우 쉽게 적용할 수 있습니다. 기본 클래스인 Assert는 기본 조건문을 테스트하는 데 사용되는 어설션을 제공합니다. StringAssert 클래스는 문자열 변수 사용 시에 도움이 되는 사용자 지정 어설션을 갖고 있습니다. 마찬가지로 CollectionAssert 클래스에는 개체 컬렉션 사용 시에 도움이 되는 어설션 메서드가 포함되어 있습니다.
표 3은 현재 릴리스의 단위 테스트 프레임워크에서 사용할 수 있는 어설션을 보여 줍니다.
표 3. VSTS 단위 테스트 프레임워크 어설션
Assert 클래스 | StringAssert 클래스 | CollectionAssert 클래스 |
---|---|---|
AreEqual() AreNotEqual() AreNotSame() AreSame() EqualsTests() Fail() GetHashCodeTests() Inconclusive() IsFalse() IsInstanceOfType() IsNotInstanceOfType() IsNotNull() IsNull() IsTrue() | Contains() DoesNotMatch() EndsWith() Matches() StartsWith() | AllItemsAreInstancesOfType() AllItemsAreNotNull() AllItemsAreUnique() AreEqual() AreEquivalent() AreNotEqual() AreNotEquivalent() Contains() DoesNotContain() IsNotSubsetOf() IsSubsetOf() |
무엇을 테스트합니까?
기본적으로 이러한 자동화된 단위 테스트는 매우 낮은 수준을 갖고 있습니다. 그래서 개체를 테스트하는 머무르지 않고 생성자, 메서드 호출 및 심지어 개체의 속성을 테스트하도록 되어 있습니다.
단위 테스트에서 "public 및 private"와 관련된 주제는 많은 논쟁거리가 됩니다. 어떤 사람들은 단위 테스트가 개체의 public 인터페이스만 테스트해야 한다고 믿고 있으며 어떤 사람들은 내부 private 메서드를 비롯한 모든 호출을 테스트해야 한다고 생각합니다. VSTS는 두 가지 수준의 단위 테스트를 모두 지원합니다. VSTS는 private 접근자나 “private” 메서드 및 속성에 기초하여 단위 테스트를 생성하는 기능을 제공하는 래퍼 클래스를 통해 private 테스트를 지원합니다.
이러한 자동화된 단위 테스트를 실행하는 것은 무엇입니까?
앞에서 언급한 것처럼 xUnit 프레임워크는 (a) 단위 테스트 실행 및 (b) 테스트 결과 보고를 담당하는 응용 프로그램으로 “테스트 실행기”라는 개념을 정의합니다. 이 문서에서는 VSTS(Visual Studio 2005 Team System)에 포함된 단위 테스트 엔진이 “테스트 실행기”로 사용됩니다. 그림 2는 BankAccountTests.cs 클래스의 실행 결과를 보여 줍니다.
그림 2. 테스트 결과(Test Results) 창: 단위 테스트 실행 결과
Microsoft Visual Studio 2005는 소스 프로젝트의 코드 모델을 사용하여 이 보기를 동적으로 채웁니다. Microsoft Visual Studio 2005는 소스 코드의 사용자 지정 특성에 기초하여 테스트 집합에 대한 정보를 동적으로 검색합니다. 표 4에는 가장 일반적인 단위 테스트 특성과 실행 순서가 나와 있습니다.
표 4. 일반적인 단위 테스트 특성
특성 | 설명 |
---|---|
TestClass() | 이 특성은 테스트 기능을 나타냅니다. |
TestMethod() | 이 특성은 테스트 사례를 나타냅니다. |
AssemblyInitialize() | 처음 실행하도록 선택한 TestClass()의 첫 번째 TestMethod()를 실행하기 전에 이 특성을 가진 메서드를 실행합니다. |
ClassInitialize() | 첫 번째 테스트를 실행하기 전에 이 특성을 가진 메서드가 호출됩니다. |
TestInitialize() | 각 TestMethod()를 실행하기 전에 이 특성을 가진 메서드가 호출됩니다. |
TestCleanup() | 각 TestMethod()를 실행한 후에 이 특성을 가진 메서드가 호출됩니다. |
ClassCleanup() | 모든 테스트를 실행한 후에 이 특성을 가진 메서드가 호출됩니다. |
AssemblyCleanup() | 처음 실행하도록 선택한 TestClass()의 마지막 TestMethod()를 실행한 후에 이 특성을 가진 메서드를 실행합니다. |
Description() | 지정된 TestMethod()에 대한 설명을 제공합니다. |
Ignore() | 어떠한 이유로 인해 TestMethod() 또는 TestClass()를 무시합니다. |
ExpectedException() | 특정 예외를 테스트할 때, 이 특성으로 지정된 예외가 구현 코드에서 발생할 경우에도 테스트 실패가 발생하지 않습니다. |
어떤 유형의 테스트를 작성합니까?
메서드 및 연관된 테스트 사이에 일대일 관계를 가지는 경우는 거의 없습니다. 자동화된 단위 테스트를 작성할 때 개발자는 새로운 방식으로 상황에 접근해야 하며 모든 상황에서 개체가 사용 및 처리되는 방법과 긍정적, 부정적 및 비확정적으로 반응하는 방법에 대한 모든 것을 알아야 합니다.
예를 들어, 데이터베이스의 고객 항목에 대해 CRUD(Create-Retrieve-Update-Delete) 기능이 있는 일반적인 개체가 있다고 가정해 봅니다. 이 개체의 Load() 메서드에 대해 다음 시나리오에 맞는 테스트를 작성하고 싶을 것입니다.
- 생성자 테스트?개체가 올바른 정보와 함께 제대로 로드되는지 확인합니다.
- PositiveLoadScalarTest?데이터베이스에 존재하는 고객의 로드 성공을 테스트합니다.
- NegativeLoadScalarTest?데이터베이스에 존재하지 않는 특정 고객의 로드 실패를 테스트합니다.
- PositiveLoadTest?알려진 데이터에 기초하여 고객의 로드 성공을 테스트합니다.
- NegativeLoadTest?데이터베이스에 존재하지 않는 여러 고객의 로드 실패를 테스트합니다.
- NegativeValidationTest?유효성 검사 논리가 제대로 작동하는지 확인합니다.
자동화된 단위 테스트 집합은 매우 다양하게 사용되며 위 내용은 그 중 일부에 불과합니다. 어떤 경우에는 구성 요소에 대한 알려진 보안 공격을 확인하기 위해 단위 테스트를 사용하기도 합니다. 좀더 넓은 관점에서 보면, 단위 테스트는 구성 요소의 정상적인 사용을 명확하게 테스트해야 합니다. 적절하게 작성된 테스트는 효율적인 소프트웨어 작성을 제대로 수행했다는 확신을 심어줍니다. 바로 이러한 확신을 줄 있는 테스트를 작성해야 합니다.
왜 코드를 생성합니까?
위의 내용을 읽고 나면 앞의 프로젝트에서 나왔던 단일 형태의 개체를 떠올리면서 이러한 개체를 사용할 경우, 많은 코드를 작성해야 한다는 점이 염려될 것입니다. 그러나 개발자는 앞서 언급한 WinForms 예제와 같은 다른 형태를 가지는 “단위 테스트” 코드를 작성합니다. 또한 더 많은 코드를 생성해야 한다는 사실보다는 재사용 가능한 문서화된 구현 예제를 가질 수 있다는 것이 더 큰 이점입니다. 이외에도 단위 테스트를 완벽하게 디자인하기 위해 많은 시간을 투자할수록 품질 보증 주기 동안에 발생하는 결함이 줄어듭니다.
앞에서 언급한 것처럼 코드 생성은 “소프트웨어를 만드는 소프트웨어”의 프로세스입니다. 반복 가능한 프로세스에 기초하여 코드를 만드는 것이 가장 이상적입니다. 예를 들어, 데이터 스크립팅, 리포지토리의 엔티티 및 해당 지속성을 나타내는 개체 작성(데이터베이스 CRUD), 데이터 유지 관리를 위한 UI 컨트롤 작성 등을 코드 생성을 사용하는 적절한 사례로 들 수 있습니다. 코드 생성을 사용할 경우의 몇 가지 이점은 다음과 같습니다.
- 시간 절약?몇 시간/일/주가 걸리던 작성 작업을 이제 몇 초/분 내에 완료할 수 있습니다.
- 표준/규칙 적용?개발 작업에서 사용자 개입이 필요한 요소를 제거하고 사용자의 규칙에 기초한 반복 가능한 프로세스에 의존하는 것은 표준 및 명명 규칙을 적용하는 데 있어 가장 좋은 방법입니다.
- Private 메서드 테스트 기능?이 문서의 앞에서 언급한 것처럼 단위 테스트 프레임워크는 “private 접근자” 클래스를 사용하여 private 메서드를 테스트하는 기능을 제공합니다. 코드 생성 엔진은 이러한 접근자 클래스와 관련된 모든 “통로 코드”를 만듭니다.
- 기존 구성 요소에 대한 지식 제공?다른 개발자의 구성 요소를 검토하는 중이십니까? 이러한 구성 요소에 기초한 코드 생성은 구현 및 개체 인터페이스에 대한 예제를 신속하게 제공할 수 있습니다. 또한 코딩을 수행하기 전에 VS 2005 Class Designer 등을 사용하여 개체의 public 인터페이스를 디자인 및 작성하는 개발자는 코드 생성 엔진이 큰 도움이 될 것입니다.
이상의 내용에서 알 수 있듯이 자동화된 단위 테스트를 위해서는 적절한 코드 생성 기능이 필요합니다. 기존의 구성 요소를 파악하고 이러한 자동화된 단위 테스트를 위한 초기 코드를 생성할 수 있다면 많은 도움이 될 것입니다 더군다나 단위 테스트 프레임워크 스텁 외에 개체의 public 인터페이스를 중심으로 작성된 구현 예제를 생성한다면 더욱 바람직할 것입니다. 모든 사람의 이러한 요구를 기대 이상으로 충족하는 것이 바로 Visual Studio 2005 Team System입니다.
코드를 몇 개 생성해 봅시다!
이 예제에서는 이 문서의 앞에 나온 BankAccount 클래스에 대한 코드를 생성하려고 합니다. 이 예제의 목적은 코드 생성 과정을 안내하며, 제공되는 기능을 살펴보고 VSTS의 단위 테스트 엔진을 사용할 경우 어떤 이점이 있는지 설명하는 데 있습니다.
1단계: 구현 코드 만들기
우선, 응용 프로그램의 비즈니스 계층으로 사용되는 클래스 라이브러리 프로젝트를 만들어 보겠습니다.
VS 2005에서 이 라이브러리를 만들려면 다음 작업을 수행합니다.
- Visual Studio 2005 베타 2를 시작합니다.
- 파일(File) 메뉴를 클릭하여 새로 만들기(New)를 클릭한 다음 프로젝트(Project)를 클릭합니다.
- 원하는 언어를 선택하고 창(Windows)을 선택한 다음 클래스 라이브러리(Class Library) 프로젝트 템플릿을 선택합니다.
- 이름(Name) 및 솔루션 이름(Solution Name)을 BankAccountDemo.Business로 설정하고 위치를 선택한 다음 확인(OK)을 클릭하여 클래스 라이브러리를 만듭니다.
클래스가 만들어진 후에 해야 할 일은 프로젝트에 맞게 설계된 BankAccount 클래스를 만드는 것입니다. 다음 작업을 수행합니다.
- 솔루션 탐색기에서 마우스 오른쪽 단추를 클릭한 다음 삭제(Delete)를 클릭하여 파일을 프로젝트에서 제거하고 디스크에서 삭제합니다.
- BankAccountDemo.Business 프로젝트를 마우스 오른쪽 단추로 클릭하고 추가(Add)를 클릭한 다음 클래스(Class)를 클릭합니다.
- 파일 이름 BankAccount.cs를 선택하고 추가(Add)를 클릭하여 클래스 파일을 만듭니다.
- BankAccount.cs 파일에서 다음과 같이 코드 변경을 수행합니다.
using System.Collections.Generic;
using System.Text;
namespace BankAccountDemo.Business
{
public class BankAccount
{
// Properties
private float _currentBalance;
public float CurrentBalance
{
get { return _currentBalance; }
}
// Constructors
public BankAccount(float initialBalance)
{
this._currentBalance = initialBalance;
}
// Methods
public void DepositMoney(float depositAmount)
{
this._currentBalance += depositAmount;
}
public void MakePayment(float paymentAmount)
{
this._currentBalance -= paymentAmount;
}
}
}
2단계: 초기 단위 테스트 코드 생성
Visual Studio 2005 Team System에서 기본 제공되는 단위 테스트 엔진을 사용하면 코드를 쉽게 생성할 수 있습니다. 단위 테스트 구조를 생성하는 것 외에도 이 엔진은 개체 작성, 형식 있는 매개 변수, 메서드 실행 등과 같은 인스턴스 관련 정보를 생성합니다.
VS 2005에서는 모든 클래스 구조 수준(네임스페이스, 클래스, 메서드, 속성, 생성자 등)에서 단위 테스트 코드를 생성할 수 있는 기능이 제공됩니다. 이러한 코드 요소를 마우스 오른쪽 단추로 클릭한 다음 테스트 생성(Generate test(s))을 클릭하면 됩니다(그림 3).
그림 3. 테스트 생성 메서드
따라서 코드 생성 프로세스를 시작하려면 다음 단계를 수행합니다.
- 클래스 이름 BankAccount를 마우스 오른쪽 단추로 클릭하고 테스트 만들기(Create Tests)를 클릭합니다.
이제 그림 4와 같은 단위 테스트 생성(Generate Unit Tests) 대화 상자가 나타나야 합니다. 이 대화 상자와 해당 구성 요소는 이 프로세스 도중에 생성된 코드를 사용자 지정할 수 있는 기능을 제공합니다. 이러한 각 요소에 대해 살펴보도록 하겠습니다.

그림 4. 단위 테스트 생성 대화 상자(큰 그림으로 보려면 이미지를 클릭)
현재 선택 항목(Current selection): 트리 보기를 사용하면 사용자 지정 클래스와 해당 요소를 탐색할 수 있습니다. VS 2005은 리플렉션을 사용하여 이 트리 보기를 채우고 사용자가 마우스 오른쪽 단추를 클릭하고 테스트 만들기(Create Tests)를 클릭한 위치에 기초하여 구성 요소를 자동으로 선택합니다. 그림 3에서는 클래스 수준에서 클릭했기 때문에 코드 생성을 위해 모든 클래스 요소가 자동으로 선택되었습니다. 개별 수준(즉, 생성자, 속성 또는 메서드)에서 생성을 선택할 경우 해당 요소만 선택됩니다.
오른쪽 상단 모서리에 있는 필터(Filter) 옵션은 트리 보기에 표시되는 결과를 수정할 수 있는 기능을 제공합니다. 그림 5에 나온 것처럼 비공개 항목 표시, 기본 형식 표시 및 내 코드만 표시 중에서 선택할 수 있습니다. 대규모 솔루션을 작업하는 중이거나 개인적인 내부 구조를 표시하면서 선택 창이 복잡해질 경우에 이 기능을 유용하게 사용할 수 있습니다.
그림 5. 선택 결과 필터링
다음은 현재 선택 항목(Current selection): 트리 보기 아래에 있는 출력 프로젝트(Output project) 목록 상자입니다. 이 목록 상자를 사용하면 생성된 테스트 기능에 대한 대상 프로젝트를 선택할 수 있습니다(그림 6). 이전에 만든 테스트 프로젝트가 솔루션에 포함된 경우 해당 테스트 프로젝트가 선택 항목에 포함됩니다. 지금은 이 대화 상자를 처음 표시한 경우이므로 새 … 테스트 프로젝트 만들기(Create a new … Test Project) 옵션을 선택할 수 있습니다.

그림 6. 출력 프로젝트 선택(큰 그림으로 보려면 이미지를 클릭)
프로세스를 계속하려면 다음 작업을 수행합니다.
- 선택한 언어에 기초하여 새 {0} 테스트 프로젝트 만들기(Create a new {0} Test Project)를 선택합니다.
마지막으로 이 대화 상자에서는 왼쪽 하단 모서리에 있는 설정(Settings) 단추를 통해 코드 생성 프로세스를 사용자 지정할 수 있습니다. 이 단추를 클릭하면 그림 7과 같은 테스트 생성 설정(Test Generation Settings) 대화 상자가 나타납니다.
그림 7. 테스트 생성 설정 대화 상자
이 대화 상자를 사용하여 다음 변경을 수행할 수 있습니다.
- 파일, 클래스(테스트 기능) 및 메서드(테스트)의 이름을 생성하는 데 사용되는 명명 규칙을 변경합니다.
- 모든 테스트 결과에 기본적으로 비확정(Inconclusive) 표시를 하는 기능을 설정/해제합니다. 이 옵션을 선택하면 생성된 각 Test() 메서드에 다음 자리 표시자 문이 포함됩니다.Assert.Inconclusive("TODO: Implement code to verify target");
- 생성 경고(즉, 코드 생성 프로세스 도중에 경고가 발생할 경우 보고하는 기능)를 활성화하는 기능을 설정/해제합니다.
- 모든 형식을 전역으로 한정합니다. 이 설정은 전역 한정자(Microsoft Visual C# 2005의 경우 global::)를 변수 선언에 추가하도록 코드 생성 엔진에 지시합니다. 여러 네임스페이스에 같은 이름의 개체가 있는 경우 이 설정을 사용합니다. 이 설정을 사용하지 않을 경우, 코드 생성 엔진은 개체를 만들기 위한 논리를 작성하지만 컴파일러는 작성할 클래스를 확인하지 못하므로 오류가 발생합니다.
- 이미 테스트가 있는 항목에 대한 테스트를 생성하는 기능을 활성화/비활성화합니다. 후속 코드 생성 시도에 대한 주제는 아래에 설명되어 있습니다.
- 문서 주석을 활성화/비활성활합니다. 이 설정을 사용하면 각 Test() 메서드 위에서 XML 문서 작성을 비활성화할 수 있습니다.
구성을 완료하고 단위 테스트 코드를 생성하려면 다음 작업을 수행합니다.
- 확인(OK) 단추를 클릭하여 코드 생성 프로세스를 시작합니다.
- 새 프로젝트의 이름으로 BankAccountDemo.Business.Test를 입력하고 만들기(Create) 단추를 클릭하여 프로세스를 완료합니다.
코드 생성 프로세스 도중의 상태를 제공하는 진행률 표시줄이 나타납니다. 몇 초 내에 프로세스가 완료되며 BankAccountTest.cs라는 클래스가 표시되어야 합니다.
무엇이 생성되었습니까?
테스트 기능을 구체적으로 검토하기 전에 코드 생성 프로세스 도중에 무엇이 만들어졌는지 살펴보도록 하겠습니다.
먼저 테스트 클래스 라이브러리 프로젝트 BankAccountDemo.Business.Test가 작성되었습니다. 구현 클래스 BankAccountDemo.Business(이 클래스에서 코드를 생성함) 및 Microsoft.VisualStudio.QualityTools.UnitTestFramework 클래스 라이브러리에 대한 참조가 프로젝트에 어떻게 포함되었는지 주의합니다. 이 클래스의 내용을 검토하는 동안 다음 파일을 볼 수 있을 것입니다.
- AuthoringTests.txt?단위 테스트를 사용하는 방법(열기, 보기, 실행, 결과 보기, 테스트 실행 방법 변경)과 VSTS에 포함된 여러 다른 테스트 유형을 정의하는 정보 콘텐츠입니다.
- ManualTest1.mht?테스트를 실행하고 결과를 보고하기 위해 VSTS에서 사용되는 수동 테스트 기능입니다. 수동 테스트는 VSTS에서 지원되는 추가 테스트 유형입니다. 자세한 내용은 MSDN Library에서 “수동 테스트” 항목을 참조하십시오.
- UnitTest1.cs?TestClass, TestInitialize, TestCleanup 및 TestMethod에 대한 정의를 비롯한 기본 단위 테스트를 제공하는 참조 클래스입니다.
- BankAccountTest.cs?어셈블리와 관련하여 생성된 단위 테스트 코드입니다. 코드 생성 프로세스의 가장 중요한 부분인 이 코드에 대해 자세히 살펴보도록 하겠습니다.
단위 테스트 엔진에 의해 생성된 클래스에는 다음 구성 요소가 포함됩니다.
- 참조된 어셈블리를 위한 Using/imports 문
- 테스트가 포함된 클래스(BankAccountTestFixture)에 대한 TestClass() 정의
- TestContext에 대한 private 접근자 및 public 속성. 현재 테스트 실행에 대한 정보와 기능을 제공하기 위해 단위 테스트 실행기(즉, VSTS 단위 테스트 프레임워크)에 사용됩니다.
- TestInitialize() 및 TestCleanup() 메서드. 이러한 메서드는 일반적으로 테스트에 필요한 개체를 획득 및 해제하는 데 사용됩니다.
- 선택한 각 메서드에 대한 TestMethod()
이제 원금과 예금액을 더한 값이 현재 잔액이 되도록 하는 DepositMoneyTest()를 살펴보도록 하겠습니다.
/// <summary>///A test case for DepositMoney (float)
///</summary>
[TestMethod()]
public void DepositMoneyTest()
{
float initialBalance = 0; // TODO: Initialize to an appropriate value
BankAccount target = new BankAccount(initialBalance);
float depositAmt = 0; // TODO: Initialize to an appropriate value
target.DepositMoney(depositAmt);
Assert.Inconclusive("A method that does not return a value" +
"cannot be verified.");
}
생성 엔진은 단순히 스텁 TestMethod() 개체를 작성하는 작업만 하는 것이 아닙니다. 생성 엔진은 다음을 비롯하여 사용자의 인터페이스에 맞는 샘플 단위 테스트를 작성했습니다.
- BankAccount 개체의 할당 및 생성(테스트의 개체 주제)
- 이 테스트의 주제로 메서드/생성자에 필요한 매개 변수를 나타내는 로컬 변수의 작성 및 기본 할당
- 매개 변수를 적절하게 할당해야 한다는 것을 개발자에게 알리는 TODO 주석
- 테스트가 소스 개체 메서드 호출에 기반을 둘 경우, 생성된 코드는 매개 변수에 대한 로컬 변수와 함께 해당 메서드에 대한 호출을 포함합니다.
- 메서드의 반환 값에 기초하는 초기 Assert() 메서드 호출
- 테스트의 코드를 완성해야 한다는 것을 알리는 Assert.Inconclusive() 메서드 호출. 비확정(inconclusive) 테스트는 테스트 결과(Test Results) 대화 상자에 오류로 표시됩니다.
생성 후에는 이제 무엇을 해야 합니까?
일반적으로 코드 생성의 이점은 같은 결과를 얻기 위해 필요했던 작업을 수행할 필요가 없다는 점에 있습니다. 이 문서의 예제에서는 다음 작업이 필요하지 않았습니다.
- 단위 테스트 프로젝트 만들기
- 프로젝트 참조 설정
- 적절한 테스트 클래스 추가
- 기초 단위 테스트 프레임워크 클래스 및 특성 작성
- 개별 테스트 메서드 만들기
- 인터페이스 특정 논리 만들기
개체의 인터페이스에 맞게 사용자 지정된 샘플 단위 테스트를 코드 생성 프로세스에서 작성했기 때문에 초기 테스트는 거의 완성된 것이라고 볼 수 있습니다. 대부분의 경우, 필요한 작업은 단지 빈 곳을 채우고 “알려진 데이터 값”을 속성 변수에 할당하여 어설션을 완성한 다음 적절한 Assert() 메서드를 만드는 것입니다. 물론, 이러한 간단한 작업이 모든 테스트에 해당하는 것은 아니며 특히 여러 어설션을 가진 복잡한 테스트에는 적용되지 않습니다.
생성된 단위 테스트 코드를 실제 테스트로 변환하는 작업은 약간의 키 입력만으로 몇 초 안에 끝낼 수 있습니다.
예를 들어, 맨 처음에는 테스트가 다음과 같다고 가정해 봅니다.
[TestMethod()]public void DepositMoneyTest()
{
float initialBalance = 0; // TODO: Initialize to an appropriate value
BankAccount target = new BankAccount(initialBalance);
float depositAmt = 0; // TODO: Initialize to an appropriate value
target.DepositMoney(depositAmt);
Assert.Inconclusive("A method that does not return a value " +
"cannot be verified.");
}
위 테스트를 쉽고 간단한 키 입력만으로 다음과 같은 완성할 수 있습니다(변경 사항은 굵게 표시됨).
[TestMethod()]public void DepositMoneyTest() {
float currentBalance = 500;
BankAccount target = new BankAccount(currentBalance);
float depositAmt = 10;
target.DepositMoney(depositAmt);
Assert.AreEqual(currentBalance + depositAmt, target.CurrentBalance,
"Deposit Test: Deposit not applied correctly");
}
단위 테스트 코드 재생성
한 가지 반가운 소식은 이전에 생성 및 수정했던 단위 테스트를 코드 생성 프로세스에서 덮어쓰지 않는다는 것입니다. Visual Studio 2005 Team System 베타 2 릴리스에서는 기존 테스트의 작성을 활성화/비활성화하는 확인란이 코드 생성 옵션에서 제공됩니다. 이 확인란을 선택했으며 동일한 이름을 가진 기존 테스트 메서드가 발견될 경우, 프로세스에서는 해당 테스트 메서드를 그대로 두고 메서드 이름의 끝에 숫자를 추가하여 후속 테스트를 만듭니다. 일반적으로 이러한 상황은 개체에서 오버로드된 메서드나 생성자를 사용하거나 기존 테스트를 선택 취소하지 않고 생성(Generate) 단추를 클릭할 경우에 발생합니다.
자동화된 단위 테스트 권장 사항
위에서는 필요한 내용을 충분히 다루었지만 단위 테스트를 만들 때 따라야 하는 다음 필수 권장 사항 사항을 알고 있다면 훨씬 더 도움이 될 것입니다.
- 단위 테스트를 독자적으로 실행할 수 있도록 서로 독립적으로 디자인합니다(테스트 UI에서 단위 테스트를 임의로 선택 또는 선택 취소할 수 있기 때문에).
- 긍정적 상태만 테스트해서는 안 됩니다. 예기치 않은 상황(예: 리소스 사용 불가, 읽기 전용 데이터베이스 등)을 비롯한 모든 시나리오에 코드가 대응할 수 있는지 확인합니다.
- 단순히 개발자가 아니라 테스터로서 QA를 염두에 둡니다. 단위 테스트를 디자인하는 데 걸리는 시간이 늘어날수록 나중에 결함을 해결하는 데 걸리는 시간이 줄어들 것입니다. 개체 사이에 데이터를 전송하는 방법, 개체를 사용하는 사람, 손쉽게 개체를 분리할 수 있는 방법, 특정 작업을 수행했을 경우의 결과 등과 같은 개체에 대한 세부 정보를 중점으로 다루어야 합니다.
- 새로운 방식으로 상황에 접근하고 가능한 많은 테스트를 다른 사람과 논의합니다. 작업이 완료되면 이전 단계로 돌아가서 혹시 누락된 것이 있는지 확인합니다. 팀 구성원으로부터 피드백을 요청합니다(예: 팀 구성원이 만든 다른 유형의 테스트는 무엇입니까?). 자신의 코드에 익숙한 개발자가 알지 못했던 사항을 다른 구성원이 제공할 수 있을 것입니다.
- VSTS 코드 적용 범위 계측(instrumentation)을 사용하여 각 테스트 실행 동안 실제로 실행된 코드 양(코드 행 수, 전체 코드에서의 비율 등)에 대한 정보를 제공합니다. 코딩이 완료되고 모든 테스트에 통과했지만 전체 논리 중에서 작은 부분만 실행되었다는 것이 코드 적용 범위를 통해 밝혀진 경우 테스트가 실제로 성공했다고 볼 수 있습니까? 물론, 코드 적용 범위 높다고 해서 완전한 “테스트” 집합을 갖고 있다는 확신할 수는 없지만 일반적으로 코드 적용 범위가 낮은 경우 새 테스트 사례가 필요할 것입니다.
- 단위 테스트를 작성할 때는 다른 사람이 코드에 더 잘 알 수 있도록 다음을 수행합니다.
- 테스트하는 어셈블리의 구조를 반영하는 프로젝트 구조를 사용합니다.
각 어셈블리는 관련 테스트 어셈블리를 가집니다.
각 클래스는 관련 테스트 클래스를 가집니다.
해당 테스트 메서드에 각 메서드 이름을 포함합니다. (즉, Load()는 PositiveLoadTest(), NegativeLoadTest(), PositiveScalarLoadTest() 등의 테스트 메서드를 가집니다.)
- 개체 속성 및 메서드의 이름을 포함하는 일관된 명령 규칙을 사용합니다.
- 테스트하는 어셈블리의 구조를 반영하는 프로젝트 구조를 사용합니다.
- 또한 오류가 있을 경우 디버깅을 수행합니다. 자동화된 단위 테스트는 디버거에서 작업하는 데 필요한 시간을 줄여줍니다. 그러나 테스트 결과와 코드 적용 범위를 통해 테스트 실패 원인을 알 수 없는 경우에는 단위 테스트를 디버깅해야 할 것입니다. Visual Studio 2005 Team System 베타 2 릴리스부터 개발자는 테스트 관리자(Test Manager)의 검사된 테스트 디버깅(Debug checked tests) 옵션으로 단위 테스트 어셈블리를 디버깅할 수 있습니다.
결론
자동화된 단위 테스트는 뛰어난 이식성과 반복 가능성을 가진 구조화 및 문서화된 프로세스를 개발 주기에 제공합니다. 기존 어셈블리를 조사하고 있거나 개발 환경에서 개발을 시작하기 전에 완전한 디자인이 요구될 경우에는 Microsoft Visual Studio 2005 Team System에서 기본 제공되는 코드 생성 엔진을 사용합니다. Visual Studio 2005 Team System의 단위 테스트 코드 생성 기능을 사용하면 작업 시간을 절약할 수 있으며 팀의 개발 표준과 규칙을 적용할 수 있습니다. 개체 작성, 매개 변수, 기본 어설션 클래스 등과 함께 테스트 메서드를 생성하는 것을 비롯한 자동화된 단위 테스트를 위한 기반을 작성함으로써 자동화된 단위 테스트를 개발 방법론에서 적절하게 적용할 수 있을 것입니다.
저자 정보
telligent 시스템의 전문 서비스 책임자인 Scott Dockendorf는 확장성과 성능이 뛰어난 .NET 응용 프로그램을 개발하고 있습니다. Scott는 솔루션 아키텍처와 보안 개발 분야에 많은 관심을 갖고 있으며 표준 및 검증된 방법론을 통해 권장된 작업 방식을 여러 기업에서 채택할 수 있도록 지원합니다. .NET 커뮤니티에서 적극적으로 활동하는 Scott는 North Dallas .NET User Group (영문)의 프로그램 책임자로 자원 봉사하고 있습니다. 또한 Scott는 현지의 .NET 사용자 그룹을 위한 세션을 제공하며 INETA의 International Academic Committee for Texas에 적극적으로 참여하고 있습니다. Scott에게 연락하려면 scottd@telligent.com으로 전자 메일을 보내거나 http://weblogs.asp.net/scottdockendorf (영문)의 웹 블로그를 방문하십시오.