다음을 통해 공유


MSTest의 데이터 기반 테스트

데이터 기반 테스트를 사용하면 여러 입력 데이터 집합으로 동일한 테스트 메서드를 실행할 수 있습니다. 각 테스트 사례에 대해 별도의 테스트 메서드를 작성하는 대신 테스트 논리를 한 번 정의하고 특성 또는 외부 데이터 원본을 통해 서로 다른 입력을 제공합니다.

개요

MSTest는 데이터 기반 테스트를 위한 몇 가지 특성을 제공합니다.

특성 사용 사례 적합한 대상
DataRow 인라인 테스트 데이터 단순 정적 테스트 사례
DynamicData 메서드, 속성 또는 필드의 데이터 복잡하거나 계산된 테스트 데이터
TestDataRow<T> 메타데이터를 사용하여 향상된 데이터 표시 이름 또는 범주가 필요한 테스트 사례
DataSource 외부 데이터 파일 또는 데이터베이스 외부 데이터 원본을 사용하는 레거시 시나리오
ITestDataSource 사용자 지정 데이터 원본 특성 완전 사용자 지정 데이터 기반 시나리오

팁 (조언)

결합 테스트(여러 매개 변수 집합의 모든 조합 테스트)의 경우 오픈 소스 Combinatorial.MSTest NuGet 패키지를 사용합니다. 이 커뮤니티 유지 관리 패키지는 GitHub에서 사용할 수 있지만 Microsoft에서 유지 관리하지 않습니다.

DataRowAttribute

이를 DataRowAttribute 통해 여러 다른 입력으로 동일한 테스트 메서드를 실행할 수 있습니다. 테스트 메서드에 하나 이상의 DataRow 특성을 적용하고 이를 TestMethodAttribute와 결합하십시오.

인수의 수 및 형식이 테스트 메서드 서명과 정확히 일치해야 합니다.

팁 (조언)

관련 분석기:

  • MSTEST0014 인수가 DataRow 테스트 메서드 서명과 일치하는지 확인합니다.
  • MSTEST0042 동일한 테스트 사례를 여러 번 실행하는 중복 DataRow 항목을 검색합니다.

기본 사용법

[TestClass]
public class CalculatorTests
{
    [TestMethod]
    [DataRow(1, 2, 3)]
    [DataRow(0, 0, 0)]
    [DataRow(-1, 1, 0)]
    [DataRow(100, 200, 300)]
    public void Add_ReturnsCorrectSum(int a, int b, int expected)
    {
        var calculator = new Calculator();
        Assert.AreEqual(expected, calculator.Add(a, b));
    }
}

지원되는 인수 형식

DataRow 는 기본 형식, 문자열, 배열 및 null 값을 비롯한 다양한 인수 형식을 지원합니다.

[TestClass]
public class DataRowExamples
{
    [TestMethod]
    [DataRow(1, "message", true, 2.0)]
    public void TestWithMixedTypes(int i, string s, bool b, float f)
    {
        // Test with different primitive types
    }

    [TestMethod]
    [DataRow(new string[] { "line1", "line2" })]
    public void TestWithArray(string[] lines)
    {
        Assert.AreEqual(2, lines.Length);
    }

    [TestMethod]
    [DataRow(null)]
    public void TestWithNull(object o)
    {
        Assert.IsNull(o);
    }

    [TestMethod]
    [DataRow(new string[] { "a", "b" }, new string[] { "c", "d" })]
    public void TestWithMultipleArrays(string[] input, string[] expected)
    {
        // Starting with MSTest v3, two arrays don't need wrapping
    }
}

변수 인수에 매개 변수 사용

키워드를 params 사용하여 가변 개수의 인수를 허용합니다.

[TestClass]
public class ParamsExample
{
    [TestMethod]
    [DataRow(1, 2, 3, 4)]
    [DataRow(10, 20)]
    [DataRow(5)]
    public void TestWithParams(params int[] values)
    {
        Assert.IsTrue(values.Length > 0);
    }
}

사용자 지정 표시 이름

DisplayName 테스트 탐색기에 테스트 사례가 표시되는 방식을 사용자 지정하도록 속성을 설정합니다.

[TestClass]
public class DisplayNameExample
{
    [TestMethod]
    [DataRow(1, 2, DisplayName = "Functional Case FC100.1")]
    [DataRow(3, 4, DisplayName = "Edge case: small numbers")]
    public void TestMethod(int i, int j)
    {
        Assert.IsTrue(i < j);
    }
}

팁 (조언)

테스트 메타데이터를 더 자세히 제어하려면 TestDataRow<T> 를 .와 함께 DynamicData사용하는 것이 좋습니다. TestDataRow<T> 는 테스트 범주와 함께 표시 이름을 지원하고 개별 테스트 사례에 대한 메시지를 무시합니다.

특정 테스트 사례 무시

MSTest v3.8부터 IgnoreMessage 속성을 사용하여 특정 데이터 행을 건너뛰십시오.

[TestClass]
public class IgnoreDataRowExample
{
    [TestMethod]
    [DataRow(1, 2)]
    [DataRow(3, 4, IgnoreMessage = "Temporarily disabled - bug #123")]
    [DataRow(5, 6)]
    public void TestMethod(int i, int j)
    {
        // Only the first and third data rows run
        // The second is skipped with the provided message
    }
}

DynamicDataAttribute

메서드 DynamicDataAttribute , 속성 또는 필드에서 테스트 데이터를 제공할 수 있습니다. 테스트 데이터가 복잡하거나 동적으로 계산되거나 인라인 DataRow 특성에 대해 너무 자세한 경우 이 특성을 사용합니다.

지원되는 데이터 원본 형식

데이터 원본은 다음 표에 나열된 유형 중 하나에 해당하는 TIEnumerable<T>로 반환할 수 있습니다. IEnumerable<T>을(를) 구현하는 모든 컬렉션은 List<T>을(를) 포함하여 T[]와 같은 배열 또는 사용자 지정 컬렉션 형식을 사용할 수 있습니다. 필요에 따라 선택합니다.

반환 형식 형식 안전성 메타데이터 지원 적합한 대상
ValueTuple(예: (int, string)) 컴파일 시간 아니오 대부분의 시나리오 - 전체 형식 검사를 사용하는 간단한 구문
Tuple<...> 컴파일 시간 아니오 사용할 수 없는 경우 ValueTuple
TestDataRow<T> 컴파일 시간 Yes 표시 이름, 범주 또는 메시지 무시가 필요한 테스트 사례
object[] 런타임만 아니오 레거시 코드 - 새 테스트 방지

팁 (조언)

새 테스트 데이터 메서드의 경우 간단한 경우 또는 ValueTuple 메타데이터가 필요한 경우에 사용합니다TestDataRow<T>. object[]은(는) 컴파일 시 형식 검사가 부족하여 형식 불일치로 인한 런타임 오류가 발생할 수 있으므로 피해야 합니다.

데이터 원본

데이터 원본은 메서드, 속성 또는 필드일 수 있습니다. 세 가지 모두 서로 교환할 수 있습니다. 기본 설정에 따라 선택합니다.

[TestClass]
public class DynamicDataExample
{
    // Method - best for computed or yielded data
    public static IEnumerable<(int Value, string Name)> GetTestData()
    {
        yield return (1, "first");
        yield return (2, "second");
    }

    // Property - concise for static data
    public static IEnumerable<(int Value, string Name)> TestDataProperty =>
    [
        (1, "first"),
        (2, "second")
    ];

    // Field - simplest for static data
    public static IEnumerable<(int Value, string Name)> TestDataField =
    [
        (1, "first"),
        (2, "second")
    ];

    [TestMethod]
    [DynamicData(nameof(GetTestData))]
    public void TestWithMethod(int value, string name)
    {
        Assert.IsTrue(value > 0);
    }

    [TestMethod]
    [DynamicData(nameof(TestDataProperty))]
    public void TestWithProperty(int value, string name)
    {
        Assert.IsTrue(value > 0);
    }

    [TestMethod]
    [DynamicData(nameof(TestDataField))]
    public void TestWithField(int value, string name)
    {
        Assert.IsTrue(value > 0);
    }
}

비고

데이터 원본 메서드, 속성 및 필드는 지원되는 형식의 public static이어야 하며, IEnumerable<T>로 반환해야 합니다.

팁 (조언)

관련 분석기: MSTEST0018 데이터 원본이 존재하고, 액세스할 수 있으며, 올바른 서명이 있는지 확인합니다.

다른 클래스의 데이터 원본

형식 매개 변수를 사용하여 다른 클래스를 지정합니다.

public class TestDataProvider
{
    public static IEnumerable<object[]> GetTestData()
    {
        yield return new object[] { 1, "first" };
        yield return new object[] { 2, "second" };
    }
}

[TestClass]
public class DynamicDataExternalExample
{
    [TestMethod]
    [DynamicData(nameof(TestDataProvider.GetTestData), typeof(TestDataProvider))]
    public void TestMethod(int value1, string value2)
    {
        Assert.IsTrue(value1 > 0);
    }
}

사용자 지정 표시 이름

속성을 사용하여 테스트 사례 표시 이름을 사용자 지정합니다.DynamicDataDisplayName

using System.Reflection;

[TestClass]
public class DynamicDataDisplayNameExample
{
    [TestMethod]
    [DynamicData(nameof(GetTestData), DynamicDataDisplayName = nameof(GetDisplayName))]
    public void TestMethod(int value1, string value2)
    {
        Assert.IsTrue(value1 > 0);
    }

    public static IEnumerable<object[]> GetTestData()
    {
        yield return new object[] { 1, "first" };
        yield return new object[] { 2, "second" };
    }

    public static string GetDisplayName(MethodInfo methodInfo, object[] data)
    {
        return $"{methodInfo.Name} with value {data[0]} and '{data[1]}'";
    }
}

비고

표시 이름 메서드는 public static여야 하며, string를 반환하고, MethodInfoobject[]의 두 매개 변수를 허용해야 합니다.

팁 (조언)

사용자 지정 표시 이름에 대한 더 간단한 방법은 별도의 메서드 대신 해당 < 속성과 함께 DisplayName를 사용하는 것이 좋습니다.

데이터 원본에서 모든 테스트 사례 무시

MSTest v3.8부터 모든 테스트 사례를 건너뛰는 데 사용합니다 IgnoreMessage .

[TestClass]
public class IgnoreDynamicDataExample
{
    [TestMethod]
    [DynamicData(nameof(GetTestData), IgnoreMessage = "Feature not ready")]
    public void TestMethod(int value1, string value2)
    {
        // All test cases from GetTestData are skipped
    }

    public static IEnumerable<object[]> GetTestData()
    {
        yield return new object[] { 1, "first" };
        yield return new object[] { 2, "second" };
    }
}

팁 (조언)

개별 테스트 사례를 무시하려면 해당 TestDataRow<T> 속성과 함께 사용합니다IgnoreMessage. TestDataRow<T> 섹션을 참조하세요.

TestDataRow

이 클래스는 TestDataRow<T> 데이터 기반 테스트의 테스트 데이터에 대한 향상된 제어를 제공합니다. 데이터 원본 반환 형식으로 IEnumerable<TestDataRow<T>>을 사용하여 다음을 지정하십시오.

  • 사용자 지정 표시 이름: 테스트 사례당 고유한 표시 이름 설정
  • 테스트 범주: 개별 테스트 사례에 메타데이터 연결
  • 메시지 무시: 이유가 있는 특정 테스트 사례 건너뛰기
  • 타입 안전 데이터: 강력한 형식의 테스트 데이터를 위한 제네릭 사용

기본 사용법

[TestClass]
public class TestDataRowExample
{
    [TestMethod]
    [DynamicData(nameof(GetTestDataRows))]
    public void TestMethod(int value1, string value2)
    {
        Assert.IsTrue(value1 > 0);
    }

    public static IEnumerable<TestDataRow> GetTestDataRows()
    {
        yield return new TestDataRow((1, "first"))
        {
            DisplayName = "Test Case 1: Basic scenario",
        };

        yield return new TestDataRow((2, "second"))
        {
            DisplayName = "Test Case 2: Edge case",
            TestCategories = ["HighPriority", "Critical"],
        };

        yield return new TestDataRow((3, "third"))
        {
            IgnoreMessage = "Not yet implemented",
        };
    }
}

DataSourceAttribute

비고

DataSource 는 .NET Framework에서만 사용할 수 있습니다. .NET (Core) 프로젝트에서는 DataRow 또는 DynamicData를 대신 사용하십시오.

테스트를 DataSourceAttribute CSV 파일, XML 파일 또는 데이터베이스와 같은 외부 데이터 원본에 연결합니다.

자세한 내용은 다음을 참조하세요.

ITestDataSource

인터페이스를 ITestDataSource 사용하면 완전히 사용자 지정 데이터 원본 특성을 만들 수 있습니다. 환경 변수, 구성 파일 또는 기타 런타임 조건을 기반으로 테스트 데이터를 생성하는 것과 같이 기본 제공 특성에서 지원하지 않는 동작이 필요한 경우 이 인터페이스를 구현합니다.

인터페이스 멤버

인터페이스는 다음 두 가지 메서드를 정의합니다.

메서드 목적
GetData(MethodInfo) 테스트 데이터를 로 반환합니다. IEnumerable<object?[]>
GetDisplayName(MethodInfo, object?[]?) 테스트 사례의 표시 이름을 반환합니다.

사용자 지정 데이터 원본 특성 만들기

사용자 지정 데이터 원본을 만들려면 Attribute을(를) 상속하고 ITestDataSource을(를) 구현하는 특성 클래스를 정의합니다.

using System.Globalization;
using System.Reflection;

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class MyDataSourceAttribute : Attribute, ITestDataSource
{
    public IEnumerable<object?[]> GetData(MethodInfo methodInfo)
    {
        // Return test data based on your custom logic
        yield return [1, "first"];
        yield return [2, "second"];
        yield return [3, "third"];
    }

    public string? GetDisplayName(MethodInfo methodInfo, object?[]? data)
    {
        return data is null
            ? null
            : string.Format(CultureInfo.CurrentCulture, "{0} ({1})", methodInfo.Name, string.Join(",", data));
    }
}

[TestClass]
public class CustomDataSourceExample
{
    [TestMethod]
    [MyDataSource]
    public void TestWithCustomDataSource(int value, string name)
    {
        Assert.IsTrue(value > 0);
        Assert.IsNotNull(name);
    }
}

실제 예제: 환경 기반 테스트 데이터

이 예제에서는 운영 체제를 기반으로 필터링하는 대상 프레임워크를 기반으로 테스트 데이터를 생성하는 사용자 지정 특성을 보여 줍니다.

using System.Globalization;
using System.Reflection;

[AttributeUsage(AttributeTargets.Method)]
public class TargetFrameworkDataAttribute : Attribute, ITestDataSource
{
    private readonly string[] _frameworks;

    public TargetFrameworkDataAttribute(params string[] frameworks)
    {
        _frameworks = frameworks;
    }

    public IEnumerable<object?[]> GetData(MethodInfo methodInfo)
    {
        bool isWindows = OperatingSystem.IsWindows();

        foreach (string framework in _frameworks)
        {
            // Skip .NET Framework on non-Windows platforms
            if (!isWindows && framework.StartsWith("net4", StringComparison.Ordinal))
            {
                continue;
            }

            yield return [framework];
        }
    }

    public string? GetDisplayName(MethodInfo methodInfo, object?[]? data)
    {
        return data is null
            ? null
            : string.Format(CultureInfo.CurrentCulture, "{0} ({1})", methodInfo.Name, data[0]);
    }
}

[TestClass]
public class CrossPlatformTests
{
    [TestMethod]
    [TargetFrameworkData("net48", "net8.0", "net9.0")]
    public void TestOnMultipleFrameworks(string targetFramework)
    {
        // Test runs once per applicable framework
        Assert.IsNotNull(targetFramework);
    }
}

팁 (조언)

표시 이름을 사용자 지정하거나 메타데이터를 추가하기만 하면 되는 간단한 경우 구현하는 대신 TestDataRow<T>DynamicData 를 사용하는 것이 좋습니다 ITestDataSource. 동적 필터링 또는 복잡한 데이터 생성 논리가 필요한 시나리오에 대한 사용자 지정 ITestDataSource 구현을 예약합니다.

전개 전략

데이터 기반 테스트 특성은 TestDataSourceUnfoldingStrategy 속성을 지원하며, 이는 테스트 탐색기와 TRX 결과에서 테스트 사례가 표시되는 방식을 제어합니다. 또한 이 속성은 개별 테스트 사례를 독립적으로 실행할 수 있는지 여부를 결정합니다.

사용 가능한 전략

전략 행동
Auto(기본값) MSTest, 최상의 전략 전개 결정
Unfold 모든 테스트 사례가 확장되고 개별적으로 표시됩니다.
Fold 모든 테스트 사례는 단일 테스트 노드로 축소됩니다.

전략을 변경해야 하는 경우

대부분의 시나리오에서 기본 Auto 동작은 최상의 균형을 제공합니다. 특정 요구 사항이 있는 경우에만 이 설정을 변경하는 것이 좋습니다.

  • 비결정적 데이터 원본
  • MSTest의 알려진 제한 사항 또는 버그
  • 많은 테스트 사례의 성능 문제

사용 예시

[TestClass]
public class UnfoldingExample
{
    [TestMethod(UnfoldingStrategy = TestDataSourceUnfoldingStrategy.Unfold)] // That's the default behavior
    [DataRow(1)]
    [DataRow(2)]
    [DataRow(3)]
    public void TestMethodWithUnfolding(int value, string text)
    {
        // Each test case appears individually in Test Explorer
    }

    [TestMethod(UnfoldingStrategy = TestDataSourceUnfoldingStrategy.Fold)]
    [DynamicData(nameof(GetData))]
    public void TestMethodWithFolding(int value, string text)
    {
        // All test cases appear as a single collapsed node
    }

    public static IEnumerable<(int, string)> GetData()
    {
        yield return (1, "one");
        yield return (2, "two");
        yield return (3, "three");
    }
}

모범 사례

  1. 올바른 특성을 선택합니다. 간단한 인라인 데이터에 사용합니다 DataRow . 복잡하거나 계산된 데이터에 사용합니다 DynamicData .

  2. 테스트 사례 이름 지정: 테스트 실패를 식별하기 쉽게 만드는 데 사용합니다 DisplayName .

  3. 데이터 원본을 가깝게 유지: 더 나은 유지 관리를 위해 가능한 경우 동일한 클래스에서 데이터 원본을 정의합니다.

  4. 의미 있는 데이터 사용: 에지 사례 및 경계 조건을 연습하는 테스트 데이터를 선택합니다.

  5. 결합 테스트를 고려합니다. 매개 변수 조합을 테스트하려면 Combinatorial.MSTest 패키지를 사용합니다.

참고하십시오