단위 테스트를 만들어 증분 코드 변경을 통해 코드가 제대로 작동하도록 합니다. 제3자가 개발한 프레임워크를 포함하여 단위 테스트를 작성하는 데 사용할 수 있는 몇 가지 프레임워크가 있습니다. 일부 테스트 프레임워크는 다른 언어 또는 플랫폼에서 테스트하기 위해 특수화되어 있습니다. 테스트 탐색기는 이러한 프레임워크에서 단위 테스트에 대한 단일 인터페이스를 제공합니다. 테스트 탐색기에 대한 자세한 내용은 테스트 탐색기 및 테스트 탐색기 FAQ를 사용하여 단위 테스트 실행을 참조하세요.
이 연습에서는 MSTest(Microsoft Test Framework)를 사용하여 C#에서 테스트된 메서드를 개발하는 방법을 보여 줍니다. 다른 언어 또는 다른 테스트 프레임워크(예: NUnit)에 맞게 쉽게 조정할 수 있습니다. 자세한 내용은 타사 단위 테스트 프레임워크 설치를 참조하세요.
테스트 만들기 및 코드 생성
.NET 또는 .NET Standard용 C# 클래스 라이브러리 프로젝트를 만듭니다. 이 프로젝트에는 테스트하려는 코드가 포함됩니다. 프로젝트 이름을 MyMath로 지정합니다.
동일한 솔루션에서 .NET용 새 MSTest 테스트 프로젝트를 추가합니다.
Visual Studio 2019 버전 16.9에서 MSTest 프로젝트 템플릿 이름은 단위 테스트 프로젝트 .
테스트 프로젝트 이름을 MathTests로 지정합니다.
테스트 프로젝트에서 특정 입력에 대해 얻은 결과를 확인하는 간단한 테스트 메서드를 작성합니다. 다음 코드를
Test1
또는UnitTest1
클래스에 추가하십시오.[TestMethod] public void BasicRooterTest() { // Create an instance to test: Rooter rooter = new Rooter(); // Define a test input and output value: double expectedResult = 2.0; double input = expectedResult * expectedResult; // Run the method under test: double actualResult = rooter.SquareRoot(input); // Verify the result: Assert.AreEqual(expectedResult, actualResult, delta: expectedResult / 100); }
테스트 코드에서 형식을 생성합니다.
커서를
Rooter
위에 놓고, 그런 다음 전구 메뉴를 엽니다.새 형식 생성을 선택합니다.
'Rooter' 형식 생성>, 새 형식 생성 선택
형식 생성 대화 상자에서 Project를 클래스 라이브러리 프로젝트인 MyMath로 설정한 다음 확인을 선택합니다.
테스트 코드에서 메서드를 생성합니다. 커서를 놓고
SquareRoot
전구 메뉴에서 'SquareRoot' 생성 메서드 를 선택하거나 'Rooter.SquareRoot' 메서드를 생성합니다.단위 테스트를 실행합니다.
테스트 탐색기를 엽니다.
테스트 메뉴에서 테스트 탐색기를 열려면 테스트 탐색기를 선택합니다.
테스트 메뉴에서 테스트 탐색기를 열려면 Windows>테스트 탐색기를 선택합니다.
테스트 탐색기에서 모두 실행 단추를 선택하여 테스트를 실행합니다.
솔루션이 빌드되고 테스트가 실행되고 실패합니다.
테스트의 이름을 선택합니다.
테스트 세부 정보는 테스트 세부 정보 요약 창에 표시됩니다.
스택 추적 아래의 위쪽 링크를 선택하여 테스트가 실패한 위치로 이동합니다.
이 시점에서 테스트가 통과되도록 수정할 수 있는 테스트 및 스텁을 만들었습니다.
코드 변경 확인
Class1.cs 파일에서 다음 코드를
SquareRoot
개선합니다.public double SquareRoot(double input) { return input / 2; }
테스트 탐색기에서 모두 실행을 선택합니다.
솔루션이 빌드되고 테스트가 실행되고 통과합니다.
입력 범위 확장
코드가 모든 경우에 작동한다는 확신을 높이려면 더 넓은 범위의 입력 값을 시도하는 테스트를 추가합니다.
팁 (조언)
통과하는 기존 테스트를 변경하지 않습니다. 대신 새 테스트를 추가합니다. 사용자 요구 사항이 변경되는 경우에만 기존 테스트를 변경합니다. 이 정책은 코드를 확장할 때 기존 기능이 손실되지 않도록 하는 데 도움이 됩니다.
테스트 클래스에서 입력 값의 범위를 시도하는 다음 테스트를 추가합니다.
[TestMethod] public void RooterValueRange() { // Create an instance to test. Rooter rooter = new Rooter(); // Try a range of values. for (double expected = 1e-8; expected < 1e+8; expected *= 3.2) { RooterOneValue(rooter, expected); } } private void RooterOneValue(Rooter rooter, double expectedResult) { double input = expectedResult * expectedResult; double actualResult = rooter.SquareRoot(input); Assert.AreEqual(expectedResult, actualResult, delta: expectedResult / 1000); }
테스트 탐색기에서 모두 실행을 선택합니다.
새 테스트는 실패합니다(첫 번째 테스트는 여전히 통과하지만). 실패 지점을 찾으려면 실패한 테스트를 선택한 다음 테스트 세부 정보 요약 창에서 세부 정보를 확인합니다.
테스트 중인 메서드를 검사하여 무엇이 잘못되었을지 확인합니다.
SquareRoot
다음과 같이 코드를 변경합니다.public double SquareRoot(double input) { double result = input; double previousResult = -input; while (Math.Abs(previousResult - result) > result / 1000) { previousResult = result; result = result - (result * result - input) / (2 * result); } return result; }
테스트 탐색기에서 모두 실행을 선택합니다.
이제 두 테스트가 모두 통과합니다.
예외적 사례에 대한 테스트 추가
부정 입력에 대한 새 테스트를 추가합니다.
[TestMethod] public void RooterTestNegativeInput() { Rooter rooter = new Rooter(); Assert.ThrowsException<ArgumentOutOfRangeException>(() => rooter.SquareRoot(-1)); }
테스트 탐색기에서 모두 실행을 선택합니다.
새 테스트가 실패합니다.
테스트 루프 아래의 메서드가 있는 경우 테스트 탐색기의 도구 모음에서 취소를 선택합니다. 테스트 실행이 중지되고 실패합니다.
메서드의
SquareRoot
시작 부분에 다음if
문을 추가하여 코드를 수정합니다.public double SquareRoot(double input) { if (input <= 0.0) { throw new ArgumentOutOfRangeException(); } ...
테스트 탐색기에서 모두 실행을 선택합니다.
모든 테스트가 통과합니다.
테스트 중인 코드 리팩터링
코드를 리팩터링하지만 테스트를 변경하지 마세요.
팁 (조언)
리팩터링이란 코드의 성능이 향상되거나 이해하기 쉽도록 하기 위한 변경 사항입니다. 코드 동작을 변경하기 위한 것이 아니므로 테스트가 변경되지 않습니다.
기능을 확장하는 단계와 별도로 리팩터링 단계를 수행하는 것이 좋습니다. 테스트를 변경하지 않고 유지하면 리팩터링하는 동안 실수로 버그를 도입하지 않았다는 확신을 얻을 수 있습니다.
메서드에서
result
계산하는SquareRoot
줄을 다음과 같이 변경합니다.public double SquareRoot(double input) { if (input <= 0.0) { throw new ArgumentOutOfRangeException(); } double result = input; double previousResult = -input; while (Math.Abs(previousResult - result) > result / 1000) { previousResult = result; result = (result + input / result) / 2; //was: result = result - (result * result - input) / (2*result); } return result; }
모두 실행을 선택하고 모든 테스트가 계속 통과하는지 확인합니다.