스토어 앱의 Visual C++ DLL 단위 테스트
이 항목에서는 Visual Studio 2012 Express for Windows 8 및 C++용 Microsoft 단위 테스트 프레임워크를 사용하여 Windows 스토어 앱의 C++ DLL에 대한 단위 테스트를 만드는 방법 중 하나에 대해 설명합니다.RooterLib DLL은 지정된 숫자의 제곱근 예상 값을 계산하는 함수를 구현하여 미적분법의 극한 이론을 보여 줍니다.이 DLL은 수학으로 할 수 있는 재미있는 작업을 사용자에게 보여 주는 Windows 스토어 앱에 포함될 수 있습니다.
참고
이 단원의 항목에서는 Windows 8용 Visual Studio 2012 Express의 기능에 대해 설명합니다. Visual Studio Ultimate, VS Premium 및 VS Professional은 단위 테스트에 대한 추가 기능을 제공합니다.
-
VS Ultimate, VS Premium 및 VS Professional에서는 Microsoft 테스트 탐색기에 대한 추가 어댑터를 만든 타사 또는 오픈 소스 단위 테스트 프레임워크를 사용할 수 있습니다.또한 테스트에 대한 코드 검사 정보를 분석하고 표시할 수도 있습니다.
-
VS Ultimate 및 VS Premium에서는 각 빌드 후 테스트를 실행할 수 있습니다.
자세한 내용은 MSDN 라이브러리의 단위 테스트를 사용하여 코드 확인을 참조하십시오.
이 항목에서는 개발의 첫 단계로 단위 테스트를 사용하는 방법을 보여 줍니다.이 방법에서는 먼저 테스트하고 있는 시스템에서 특정 동작을 확인하는 테스트 메서드를 작성한 다음 테스트를 통과하는 코드를 작성합니다.다음 절차의 순서를 변경함으로써 이 전략을 반대로 적용하여 먼저 테스트할 코드를 작성한 다음 단위 테스트를 작성할 수 있습니다.
또한 이 항목에서는 단일 Visual Studio 솔루션과 테스트할 DLL 및 단위 테스트에 대한 별도의 프로젝트를 만듭니다.DLL 프로젝트에 직접 단위 테스트를 포함하거나 단위 테스트 및 .DLL에 대한 별도의 솔루션을 만들 수도 있습니다.사용할 구조에 대한 자세한 내용은 테스트 탐색기를 사용하여 기존 C++ 응용 프로그램 단위 테스트를 참조하십시오.
항목 내용
이 항목에서는 다음 작업에 대해 설명합니다.
솔루션 및 단위 테스트 프로젝트 만들기
테스트가 테스트 탐색기에서 실행되는지 확인
솔루션에 DLL 프로젝트 추가
dll 프로젝트에 테스트 프로젝트 연결
반복적으로 테스트를 확장하고 통과하도록 만들기
실패한 테스트 디버깅
테스트를 변경하지 않고 코드 리팩터링
솔루션 및 단위 테스트 프로젝트 만들기
파일 메뉴에서 새로 만들기를 선택한 다음 새 프로젝트를 선택합니다.
새 프로젝트 대화 상자에서 설치됨을 확장한 다음 **Visual C++**를 확장하고 Windows 스토어를 선택합니다.그런 다음 프로젝트 템플릿 목록에서 **단위 테스트 라이브러리(Windows 스토어 앱)**를 선택합니다.
프로젝트의 이름을 RooterLibTests로 지정하고 위치를 지정한 다음 솔루션의 이름을 RooterLib로 지정하고 솔루션용 디렉터리 만들기가 선택되어 있는지 확인합니다.
새 프로젝트에서 unittest1.cpp를 엽니다.
다음 사항에 유의합니다.
각 테스트는 TEST_METHOD(YourTestName){...}를 사용하여 정의됩니다.
일반적인 함수 시그니처를 작성할 필요가 없습니다.시그니처는 TEST_METHOD 매크로에 의해 만들어집니다.이 매크로는 void를 반환하는 인스턴스 함수를 생성합니다.또한 테스트 메서드에 대한 정보를 반환하는 정적 함수도 생성합니다.테스트 탐색기는 이 정보를 사용하여 메서드를 찾을 수 있습니다.
테스트 메서드는 TEST_CLASS(YourClassName){...}를 사용하여 클래스로 그룹화됩니다.
테스트가 실행될 때 각 테스트 클래스의 인스턴스가 만들어집니다.테스트 메서드는 지정되지 않은 순서로 호출됩니다.각 모듈, 클래스 또는 메서드 전후에 호출되는 특수 메서드를 정의할 수 있습니다.자세한 내용은 MSDN 라이브러리의 Microsoft.VisualStudio.TestTools.CppUnitTestFramework 사용을 참조하십시오.
테스트가 테스트 탐색기에서 실행되는지 확인
다음 테스트 코드를 삽입합니다.
TEST_METHOD(TestMethod1) { Assert::AreEqual(1,1); }
Assert 클래스는 테스트 메서드에서 결과를 확인하는 데 사용할 수 있는 몇 가지 정적 메서드를 제공합니다.
테스트 메뉴에서 실행을 선택한 다음 모두 실행을 선택합니다.
테스트 프로젝트가 빌드되고 실행됩니다.테스트 탐색기 창이 나타나고 테스트가 통과한 테스트 아래에 나열됩니다.창의 아래쪽에 있는 요약 창은 선택된 테스트에 대한 추가 정보를 제공합니다.
솔루션에 DLL 프로젝트 추가
솔루션 탐색기에서 솔루션 이름을 선택합니다.바로 가기 메뉴에서 추가를 선택한 다음 새 프로젝트 추가를 선택합니다.
새 프로젝트 추가 대화 상자에서 **DLL(Windows 스토어 앱)**을 선택합니다.
RooterLib.h 파일에 다음 코드를 추가합니다.
// The following ifdef block is the standard way of creating macros which make exporting // from a DLL simpler. All files within this DLL are compiled with the ROOTERLIB_EXPORTS // symbol defined on the command line. This symbol should not be defined on any project // that uses this DLL. This way any other project whose source files include this file see // ROOTERLIB_API functions as being imported from a DLL, whereas this DLL sees symbols // defined with this macro as being exported. #ifdef ROOTERLIB_EXPORTS #define ROOTERLIB_API __declspec(dllexport) #else #define ROOTERLIB_API __declspec(dllimport) #endif //ROOTERLIB_EXPORTS class ROOTERLIB_API CRooterLib { public: CRooterLib(void); double SquareRoot(double v); };
주석에서는 이 DLL의 개발자뿐만 아니라 프로젝트에서 이 DLL을 참조하는 모든 사용자를 대상으로 ifdef 블록에 대해 설명합니다.DLL의 프로젝트 속성을 사용하여 명령줄에 ROOTERLIB_EXPORTS 기호를 추가할 수 있습니다.
CRooterLib 클래스는 생성자와 SqareRoot 평가자 메서드를 선언합니다.
명령줄에 ROOTERLIB_EXPORTS 기호를 추가합니다.
솔루션 탐색기에서 RooterLib 프로젝트를 선택한 다음 바로 가기 메뉴에서 속성을 선택합니다.
RooterLib 속성 페이지 대화 상자에서 구성 속성을 확장하고 **C++**를 확장한 다음 전처리기를 선택합니다.
전처리기 정의 목록에서 **<편집...>**을 선택한 다음 전처리기 정의 대화 상자에서 ROOTERLIB_EXPORTS를 추가합니다.
선언된 함수의 최소 구현을 추가합니다.RooterLib.cpp를 열고 다음 코드를 추가합니다.
// constructor CRooterLib::CRooterLib() { } // Find the square root of a number. double CRooterLib::SquareRoot(double v) { return 0.0; }
dll 프로젝트에 테스트 프로젝트 연결
RooterLibTests 프로젝트에 RooterLib를 추가합니다.
솔루션 탐색기에서 RooterLibTests 프로젝트를 선택한 다음 바로 가기 메뉴에서 **참조...**를 선택합니다.
RooterLib 프로젝트 속성 대화 상자에서 공용 속성을 확장하고 프레임워크 및 참조를 선택합니다.
**새 참조 추가...**를 선택합니다.
참조 추가 대화 상자에서 솔루션을 확장하고 프로젝트를 선택한 다음RouterLib 항목을 선택합니다.
unittest1.cpp에 RooterLib 헤더 파일을 포함합니다.
unittest1.cpp를 엽니다.
#include "CppUnitTest.h" 줄 아래에 다음 코드를 추가합니다.
#include "..\RooterLib\RooterLib.h"
가져온 함수를 사용하는 테스트를 추가합니다.다음 코드를 unittest1.cpp에 추가합니다.
TEST_METHOD(BasicTest) { CRooterLib rooter; Assert::AreEqual( // Expected value: 0.0, // Actual value: rooter.SquareRoot(0.0), // Tolerance: 0.01, // Message: L"Basic test failed", // Line number - used if there is no PDB file: LINE_INFO()); }
솔루션을 빌드합니다.
새 테스트가 테스트 탐색기의 실행하지 않은 테스트 노드에 표시됩니다.
테스트 탐색기에서 모두 실행을 선택합니다.
테스트와 코드 프로젝트를 설정했고 코드 프로젝트의 기능을 실행하는 테스트를 실행할 수 있음을 확인했습니다.이제 실제 테스트 및 코드의 작성을 시작할 수 있습니다.
반복적으로 테스트를 확장하고 통과하도록 만들기
새 테스트를 추가합니다.
TEST_METHOD(RangeTest) { CRooterLib rooter; for (double v = 1e-6; v < 1e6; v = v * 3.2) { double expected = v; double actual = rooter.SquareRoot(v*v); double tolerance = expected/1000; Assert::AreEqual(expected, actual, tolerance); } };
팁
통과한 테스트는 변경하지 않는 것이 좋습니다.대신 새 테스트를 추가하고 테스트가 통과하도록 코드를 업데이트한 다음 다른 테스트를 추가하는 식으로 작업합니다.
사용자가 요구 사항을 변경하는 경우 더 이상 올바르지 않은 테스트를 비활성화합니다.새 테스트를 작성하고 동일한 증분 방식으로 한 번에 하나씩 작동하게 만듭니다.
테스트 탐색기에서 모두 실행을 선택합니다.
테스트가 실패합니다.
팁
테스트를 작성한 후 즉시 각 테스트가 실패하는지 확인합니다.이렇게 하면 절대로 실패하지 않는 테스트를 작성하기 위해 쉬운 실수를 방지하는 데 도움이 됩니다.
새 테스트가 통과하도록 테스트 중인 코드를 개선합니다.다음 코드를 RooterLib.cpp에 추가합니다.
#include <math.h> ... // Find the square root of a number. double CRooterLib::SquareRoot(double v) { double result = v; double diff = v; while (diff > result/1000) { double oldResult = result; result = result - (result*result - v)/(2*result); diff = abs (oldResult - result); } return result; }
솔루션을 빌드한 다음 테스트 탐색기에서 모두 실행을 선택합니다.
두 테스트가 모두 통과합니다.
팁
한 번에 하나씩 테스트를 추가하여 코드를 개발합니다.각 반복 후 모든 테스트가 통과하는지 확인합니다.
실패한 테스트 디버깅
다른 테스트를 unittest1.cpp에 추가합니다.
// Verify that negative inputs throw an exception. TEST_METHOD(NegativeRangeTest) { wchar_t message[200]; CRooterLib rooter; for (double v = -0.1; v > -3.0; v = v - 0.5) { try { // Should raise an exception: double result = rooter.SquareRoot(v); swprintf_s(message, L"No exception for input %g", v); Assert::Fail(message, LINE_INFO()); } catch (std::out_of_range ex) { continue; // Correct exception. } catch (...) { swprintf_s(message, L"Incorrect exception for %g", v); Assert::Fail(message, LINE_INFO()); } } };
테스트 탐색기에서 모두 실행을 선택합니다.
테스트가 실패합니다.테스트 탐색기에서 테스트 이름을 선택합니다.실패한 어설션이 강조 표시됩니다.실패 메시지가 테스트 탐색기의 세부 정보 창에 표시됩니다.
테스트가 실패하는 이유를 보려면 함수를 단계별로 실행합니다.
SquareRoot 함수의 시작 부분에 중단점을 설정합니다.
실패한 테스트의 바로 가기 메뉴에서 선택한 테스트 디버그를 선택합니다.
실행이 중단점에서 멈추면 코드를 단계별로 실행합니다.
예외를 catch하도록 다음 코드를 RooterLib.cpp에 추가합니다.
#include <stdexcept> ... double CRooterLib::SquareRoot(double v) { //Validate the input parameter: if (v < 0.0) { throw std::out_of_range("Can't do square roots of negatives"); } ...
테스트 탐색기에서 모두 실행을 선택하여 수정된 메서드를 테스트하고 실패가 재발하지 않는지 확인합니다.
이제 테스트가 모두 통과합니다.
테스트를 변경하지 않고 코드 리팩터링
SquareRoot 함수에서 중앙 계산을 단순화합니다.
// old code //result = result - (result*result - v)/(2*result); // new code result = (result + v/result) / 2.0;
모두 실행을 선택하여 리팩터링된 메서드를 테스트하고 실패가 재발하지 않는지 확인합니다.
팁
훌륭한 단위 테스트의 안정적인 집합은 코드를 변경할 때 버그를 만들지 않았다는 확신을 줍니다.
리팩터링을 다른 변경 내용과 분리합니다.