dotnet test 및 xUnit을 사용하여 .NET에서 C# 단위 테스트

이 자습서에서는 단위 테스트 프로젝트 및 소스 코드 프로젝트를 포함하는 솔루션을 빌드하는 방법을 보여줍니다. 미리 빌드된 솔루션을 사용하여 이 자습서를 진행하려면 샘플 코드를 보거나 다운로드합니다. 다운로드 지침은 샘플 및 자습서를 참조하세요.

솔루션 만들기

이 섹션에서는 원본 및 테스트 프로젝트를 포함하는 솔루션을 만듭니다. 완료된 솔루션은 다음과 같은 디렉터리 구조를 갖습니다.

/unit-testing-using-dotnet-test
    unit-testing-using-dotnet-test.sln
    /PrimeService
        PrimeService.cs
        PrimeService.csproj
    /PrimeService.Tests
        PrimeService_IsPrimeShould.cs
        PrimeServiceTests.csproj

다음 지침에서는 테스트 솔루션을 만드는 단계를 제공합니다. 한 단계로 테스트 솔루션을 만드는 방법에 대한 지침은 테스트 솔루션을 만드는 명령을 참조하세요.

  • 셸 창을 엽니다.

  • 다음 명령을 실행합니다.

    dotnet new sln -o unit-testing-using-dotnet-test
    

    dotnet new sln 명령은 unit-testing-using-dotnet-test 디렉터리에서 새 솔루션을 만듭니다.

  • 디렉터리를 unit-testing-using-dotnet-test 폴더로 변경합니다.

  • 다음 명령을 실행합니다.

    dotnet new classlib -o PrimeService
    

    dotnet new classlib 명령은 PrimeService 폴더에 새 클래스 라이브러리 프로젝트를 만듭니다. 새 클래스 라이브러리에는 테스트할 코드가 포함됩니다.

  • Class1.cs의 이름을 PrimeService.cs로 바꿉니다.

  • PrimeService.cs의 코드를 다음 코드로 바꿉니다.

    using System;
    
    namespace Prime.Services
    {
        public class PrimeService
        {
            public bool IsPrime(int candidate)
            {
                throw new NotImplementedException("Not implemented.");
            }
        }
    }
    
  • 앞의 코드가 하는 역할은 다음과 같습니다.

    • 구현되지 않았음을 나타내는 메시지와 함께 NotImplementedException을 throw합니다.
    • 자습서의 뒷부분에서 업데이트됩니다.
  • unit-testing-using-dotnet-test 디렉터리에서 다음 명령을 실행하여 클래스 라이브러리 프로젝트를 솔루션에 추가합니다.

    dotnet sln add ./PrimeService/PrimeService.csproj
    
  • 다음 명령을 실행하여 PrimeService.Tests 프로젝트를 만듭니다.

    dotnet new xunit -o PrimeService.Tests
    
  • 이전 명령:

    • PrimeService.Tests 디렉터리에 PrimeService.Tests 프로젝트를 만듭니다. 테스트 프로젝트는 xUnit을 테스트 라이브러리로 사용합니다.
    • 프로젝트 파일에 다음 <PackageReference /> 요소를 추가하여 Test Runner를 구성합니다.
      • Microsoft.NET.Test.Sdk
      • xunit
      • xunit.runner.visualstudio
      • coverlet.collector
  • 다음 명령을 실행하여 솔루션 파일에 테스트 프로젝트를 추가합니다.

    dotnet sln add ./PrimeService.Tests/PrimeService.Tests.csproj
    
  • PrimeService 클래스 라이브러리를 PrimeService.Tests 프로젝트에 대한 종속성으로 추가합니다.

    dotnet add ./PrimeService.Tests/PrimeService.Tests.csproj reference ./PrimeService/PrimeService.csproj  
    

솔루션을 만드는 명령

이 섹션에는 이전 섹션의 모든 명령이 요약되어 있습니다. 이전 섹션의 단계를 완료한 경우에는 이 섹션을 건너뜁니다.

다음 명령은 Windows 머신에서 테스트 솔루션을 만듭니다. macOS 및 Unix의 경우 ren 명령을 ren OS 버전으로 업데이트하여 파일 이름을 바꿉니다.

dotnet new sln -o unit-testing-using-dotnet-test
cd unit-testing-using-dotnet-test
dotnet new classlib -o PrimeService
ren .\PrimeService\Class1.cs PrimeService.cs
dotnet sln add ./PrimeService/PrimeService.csproj
dotnet new xunit -o PrimeService.Tests
dotnet add ./PrimeService.Tests/PrimeService.Tests.csproj reference ./PrimeService/PrimeService.csproj
dotnet sln add ./PrimeService.Tests/PrimeService.Tests.csproj

이전 섹션에 있는 "PrimeService.cs의 코드를 다음 코드로 바꾸기" 지침을 따릅니다.

테스트 만들기

TDD(테스트 기반 개발)에서 널리 사용되는 방식은 대상 코드를 구현하기 전에 (실패하는) 테스트를 작성하는 것입니다. 이 자습서에서는 TDD 방식을 사용합니다. IsPrime 메서드는 호출할 수 있지만 구현되지 않습니다. IsPrime에 대한 테스트 호출이 실패합니다. TDD를 사용하면 실패하는 것으로 알려진 테스트가 작성됩니다. 테스트가 통과하도록 대상 코드가 업데이트됩니다. 이 방식을 계속 반복하여 실패한 테스트를 작성한 후 대상 코드를 업데이트하여 통과합니다.

PrimeService.Tests 프로젝트 업데이트:

  • PrimeService.Tests/UnitTest1.cs를 삭제합니다.
  • PrimeService.Tests/PrimeService_IsPrimeShould.cs 파일을 만듭니다.
  • PrimeService_IsPrimeShould.cs의 코드를 다음 코드로 바꿉니다.
using Xunit;
using Prime.Services;

namespace Prime.UnitTests.Services
{
    public class PrimeService_IsPrimeShould
    {
        [Fact]
        public void IsPrime_InputIs1_ReturnFalse()
        {
            var primeService = new PrimeService();
            bool result = primeService.IsPrime(1);

            Assert.False(result, "1 should not be prime");
        }
    }
}

[Fact] 특성은 Test Runner에서 실행하는 테스트 메서드를 선언합니다. Primeservice.tests 폴더에서 dotnet test를 실행합니다. dotnet test 명령은 두 프로젝트를 모두 빌드하고 테스트를 실행합니다. xUnit Test Runner에는 테스트를 실행할 프로그램 진입점이 포함되어 있습니다. dotnet test는 단위 테스트 프로젝트를 사용하여 Test Runner를 시작합니다.

IsPrime이 구현되지 않았기 때문에 테스트가 실패합니다. TDD 방식을 사용하여 이 테스트가 통과할 수 있는 코드만 작성합니다. 다음 코드를 사용하여 IsPrime을 업데이트합니다.

public bool IsPrime(int candidate)
{
    if (candidate == 1)
    {
        return false;
    }
    throw new NotImplementedException("Not fully implemented.");
}

dotnet test를 실행합니다. 테스트가 통과됩니다.

더 많은 테스트 추가

0 및 -1에 대한 소수 테스트를 추가합니다. 이전 단계에서 만든 테스트를 복사하고 다음 코드를 복사하여 0과 -1을 테스트할 수 있습니다. 하지만 더 나은 방법이 있으므로 그렇게 하지 마세요.

var primeService = new PrimeService();
bool result = primeService.IsPrime(1);

Assert.False(result, "1 should not be prime");

매개 변수만 변경될 때 테스트 코드를 복사하면 코드 중복 및 테스트 블로트가 발생합니다. 다음 xUnit 특성은 유사한 테스트 모음을 작성하는 데 사용할 수 있습니다.

  • [Theory]는 같은 코드를 실행하지만, 다른 입력 인수가 포함된 테스트 모음을 나타냅니다.
  • [InlineData] 특성은 해당 입력에 대한 값을 지정합니다.

새 테스트를 만들지 않고 앞의 xUnit 특성을 적용하여 단일 이론을 만듭니다. 다음 코드를

[Fact]
public void IsPrime_InputIs1_ReturnFalse()
{
    var primeService = new PrimeService();
    bool result = primeService.IsPrime(1);

    Assert.False(result, "1 should not be prime");
}

다음 코드를 사용합니다.

[Theory]
[InlineData(-1)]
[InlineData(0)]
[InlineData(1)]
public void IsPrime_ValuesLessThan2_ReturnFalse(int value)
{
    var result = _primeService.IsPrime(value);

    Assert.False(result, $"{value} should not be prime");
}

앞의 코드에서 [Theory][InlineData]를 사용하여 2보다 작은 몇 가지 값을 테스트할 수 있습니다. 2는 가장 작은 소수입니다.

클래스 선언 뒤와 [Theory] 특성 앞에 다음 코드를 추가합니다.

private readonly PrimeService _primeService;

public PrimeService_IsPrimeShould()
{
    _primeService = new PrimeService();
}

dotnet test를 실행합니다. 그러면 테스트 중 2개가 실패합니다. 모든 테스트를 통과하도록 하려면 다음 코드를 사용하여 IsPrime 메서드를 업데이트합니다.

public bool IsPrime(int candidate)
{
    if (candidate < 2)
    {
        return false;
    }
    throw new NotImplementedException("Not fully implemented.");
}

TDD 방식에 따라 실패하는 테스트를 더 추가한 후 대상 코드를 업데이트합니다. 테스트의 완료된 버전라이브러리의 완전한 구현을 참조하세요.

완료된 IsPrime 메서드는 소수판별 테스트를 위한 효율적인 알고리즘이 아닙니다.

추가 리소스