Search Results for

    Show / Hide Table of Contents

    TestCase

    TestCaseAttribute marks a method with parameters as a test and supplies inline arguments for individual cases. You may apply the attribute multiple times to create several cases from one method.

    [TestCase(12, 3, 4)]
    [TestCase(12, 2, 6)]
    [TestCase(12, 4, 3)]
    public void DivideTest(int n, int d, int q)
    {
        Assert.That(n / d, Is.EqualTo(q));
    }
    

    The three [TestCase(...)] lines above yield three invocations—one set of arguments per attribute.

    Note

    Because arguments to .NET attributes are limited in terms of the types that may be used, NUnit attempts to convert literal values using Convert.ChangeType() before passing them into the method.

    TestCaseAttribute may appear one or more times on a test method, which may also carry other data-providing attributes. Once any parameter has explicit data ([TestCase], [Values], [Range], …), every parameter must have a data source—see Parameterized Tests. The method may optionally be marked with the Test attribute as well.

    Constructors

    NUnit supplies arguments to the test method from the positional constructor parameters. You may repeat the attribute on the same method to provide multiple cases.

    TestCaseAttribute(params object?[]? arguments)
    TestCaseAttribute(object? arg)
    TestCaseAttribute(object? arg1, object? arg2)
    TestCaseAttribute(object? arg1, object? arg2, object? arg3)
    

    On .NET 6+, generic forms such as TestCaseAttribute<T> offer compile-time typed arguments (see Generic TestCase Attributes below).

    Use named parameters on the attribute for per-case metadata (ExpectedResult, Ignore, Explicit, etc.).

    Applies To

    Test Methods Test Fixtures (Classes) Assembly
    ✅ ❌ ❌

    Expected result

    When the method returns a value, ExpectedResult lets NUnit compare the return value instead of asserting inside the body:

    [TestCase(12, 3, ExpectedResult = 4)]
    [TestCase(12, 2, ExpectedResult = 6)]
    [TestCase(12, 4, ExpectedResult = 3)]
    public int DivideTest(int n, int d)
    {
        return n / d;
    }
    

    For those cases NUnit asserts that the return value equals the ExpectedResult specified on that attribute instance (rather than inspecting the attribute value inside the body).

    Named parameters overview

    Beyond arguments and ExpectedResult, each TestCase can set metadata and filters:

    Named parameter Role
    Author Author metadata for this case.
    Category Comma-separated categories for this case only (not the whole fixture).
    Description Case description surfaced in runners and XML.
    ExcludePlatform Comma-separated platform identifiers to skip this case—see Platform.
    ExpectedResult Expected return value; method must declare a compatible return type.
    Explicit Makes this single case explicit; pair with Reason for messaging.
    Ignore / IgnoreReason Ignores just this case; both set the skip reason (Ignore is an alias setter).
    IncludePlatform Comma-separated identifiers where this case is allowed—see Platform.
    Reason Explanation for Explicit (or propagated as skip-reason metadata).
    TestName Custom template for this case display name—see Template Based Test Naming.
    TestOf Documents the tested type (metadata only; not enforced by runners).
    TypeArgs Explicit generic type arguments (NUnit 4.1+).
    Until Time-boxed ignore; requires IgnoreReason—after the instant passes, the case runs normally (same rules as Ignore(..., Until = ...)).

    Sections below reference runnable snippets for most switches. Some parameters (Ignore, Explicit, Category) are easy to misapply as separate attributes on the fixture—see Naming collisions after the examples.

    Description, Author, and TestOf

    [TestFixture]
    public sealed class DescriptionAuthorTestOfExample
    {
        [TestCase(2,
            ExpectedResult = 4,
            Description = "Verify doubling for integer input",
            Author = "Snippet Author",
            TestOf = typeof(StringBuilder))] // System.Text (implicit usings)
        public int DoublesCorrectly(int n)
        {
            return n * 2;
        }
    }
    

    Include / exclude platforms

    Per-case platform filters mirror the Platform attribute. The sample uses ExcludePlatform so it keeps running on typical desktops; an IncludePlatform idea appears in a comment because it can legitimately skip hosts that lack a moniker:

    [TestFixture]
    public sealed class PlatformFilteringExample
    {
        /// <summary>Runs on all hosts; baseline case.</summary>
        [TestCase(1)]
    
        /// <summary>Usually still runs—excludes Xbox moniker only (see <c>Platform</c> attribute docs for names).</summary>
        [TestCase(2, ExcludePlatform = "XBox")]
    
        // IncludePlatform example (may skip on some hosts): [TestCase(3, IncludePlatform = "Net,Linux")]
        public void PlatformAwareCases(int id)
        {
            Assert.That(id, Is.InRange(1, 2));
        }
    }
    

    Custom TestName templates

    Use template tokens such as {m}, {c}, {a}, {0}—see Template Based Test Naming:

    [TestFixture]
    public sealed class CustomNamesExample
    {
        [TestCase(10, ExpectedResult = 20, TestName = "Doubling_Positive_Value({a})")]
        [TestCase(-3, ExpectedResult = -6, TestName = "Doubling_Negative_Value({a})")]
        public int DoubleForDisplay(int input)
        {
            return input * 2;
        }
    }
    

    Ignore, Explicit, and Category (named parameters)

    Warning

    Ignore, Explicit, and Category must appear as named properties on [TestCase(...)]. A separate [Ignore] / [Explicit] line still decorates the entire fixture. See Naming collisions below.

    [TestCase(1, 1)]
    [TestCase(0, 0, Ignore = "Only ignore this")]
    [TestCase(1, 3)]
    public void AddTestWithIgnore(int a, int b)
    {
        var result = a + b;
        Assert.That(result, Is.GreaterThan(1));
    }
    
    [TestCase(1, 1)]
    [TestCase(0, 0, Explicit = true, Reason = "This will fail so only run explicitly")]
    [TestCase(1, 3)]
    public void AddTestWithExplicit(int a, int b)
    {
        var result = a + b;
        Assert.That(result, Is.GreaterThan(1));
    }
    

    Reason is optional alongside Explicit = true; some runners omit or obscure explicit reasons—for example Visual Studio Test Explorer often does not show the explanatory text even when supplied.

    [TestCase(1, 1)]
    [TestCase(2, 0, Category = "Crazy")]
    [TestCase(1, 3)]
    public void AddTestWithCategory(int a, int b)
    {
        var result = a + b;
        Assert.That(result, Is.GreaterThan(1));
    }
    

    Putting [Category] on the fixture (or elsewhere on the test type) assigns categories to all tests under that fixture, whereas Category = "..." on [TestCase] scopes the category to that generated case.

    Until with IgnoreReason

    Until follows the same constraints as Ignore(Until = ...): IgnoreReason is mandatory—without it, NUnit marks the metadata invalid.

    [TestFixture]
    public sealed class IgnoreUntilExample
    {
        // Until requires IgnoreReason; expired dates revert to runnable (see Ignore attribute docs).
    
        [TestCase(99, IgnoreReason = "Stale doc sample ignore window", Until = "2001-01-01 12:00:00Z")]
        [TestCase(1)]
        public void IgnoreExpires(int value)
        {
            Assert.That(value, Is.Positive);
        }
    }
    

    TypeArgs for generic test methods

    Runtime type substitution works on .NET Framework and modern .NET:

    [TestFixture]
    public sealed class TypeArgsRuntimeExample
    {
        [TestCase(42, TypeArgs = new[] { typeof(int) })]
        [TestCase("sample", TypeArgs = new[] { typeof(string) })]
        public void GenericViaTypeArgs<T>(T payload)
        {
            Assert.That(payload, Is.Not.Null);
            Assert.That(typeof(T), Is.EqualTo(payload!.GetType()));
        }
    }
    

    Choosing TypeArgs vs generic [TestCase<…>]

    [TestFixture]
    public sealed class TypeArgsVsCompileTimeGeneric
    {
        /// <summary>Specify generic arguments at runtime (<c>TypeArgs</c>). Works on .NET Framework.</summary>
        [TestCase(42, TypeArgs = new Type[] { typeof(int) })]
        [TestCase("literal", TypeArgs = new Type[] { typeof(string) })]
        public void ViaTypeArgs<T>(T value)
        {
            Assert.That(value, Is.InstanceOf<T>());
        }
    
        /// <summary>Prefer on .NET 6+ where generic attributes are available (NUnit 4.6+).</summary>
        [TestCase<int>(2112)]
        [TestCase<double>(42.42)]
        public void ViaGenericAttribute<T>(T sample)
        {
            Assert.That(sample, Is.InstanceOf<T>());
        }
    }
    

    TypeArgs keeps samples working on .NET Framework hosts. [TestCase<int>(…)] (through five type parameters) needs .NET 6+ generic attribute support (NUnit 4.6+).

    Naming collisions

    Adding a second [Ignore] attribute beside [TestCase] binds to the enclosing fixture—even if visually on the same or next line—as if you ignored the entire class.

    Correct TestCase Ignore usage

    The wrong layouts below show how easy it is to ignore the fixture by accident: (1) an extra [Ignore] on the same line after [TestCase], (2) the fixture disappears as ignored tests, (3) a separate [Ignore] line behaves the same as (1).

    Wrong TestCase Ignore usage

    Thanks to Geir Marius Gjul for raising this question again.

    Order of Execution

    Individual test cases are executed in the order in which NUnit discovers them. This order does not necessarily follow the lexical order of the attributes and will often vary between different compilers or different versions of the CLR.

    As a result, when TestCaseAttribute appears multiple times on a method or when other data-providing attributes are used in combination with TestCaseAttribute, the order of the test cases is undefined.

    Generic TestCase Attributes

    NUnit provides generic versions of TestCaseAttribute that offer compile-time type safety for test arguments. These are available as TestCaseAttribute<T> through TestCaseAttribute<T1, T2, T3, T4, T5>, supporting up to five type parameters.

    Note

    From NUnit 4.6, generic TestCase<> attributes require runtimes with generic attribute support (not classic .NET Framework). Use TypeArgs there instead.

    Single Type Parameter

    // Testing with a single generic type parameter
    // The compiler ensures the argument matches the type
    [TestCase<int>(42)]
    [TestCase<int>(0)]
    [TestCase<int>(-1)]
    public void TestDefaultComparison<T>(T value)
    {
        Assert.That(value, Is.InstanceOf<T>());
    }
    

    Multiple Type Parameters

    // Testing with two generic type parameters
    [TestCase<int, double>(42, 42.0)]
    [TestCase<string, int>("hello", 5)]
    public void TestPairTypes<T1, T2>(T1 first, T2 second)
    {
        Assert.That(first, Is.InstanceOf<T1>());
        Assert.That(second, Is.InstanceOf<T2>());
    }
    

    With Expected Result

    Generic TestCase attributes support all the same named parameters as the regular TestCaseAttribute:

    // Using ExpectedResult with generic TestCase
    [TestCase<int>(5, ExpectedResult = 10)]
    [TestCase<int>(0, ExpectedResult = 0)]
    [TestCase<int>(-3, ExpectedResult = -6)]
    public int DoubleValue<T>(T value) where T : IConvertible
    {
        return Convert.ToInt32(value) * 2;
    }
    

    Mixing Generic and Regular TestCase

    You can mix generic and regular TestCase attributes on the same method:

    // You can mix regular and generic TestCase attributes
    [TestCase(1, 2)]                        // Types inferred
    [TestCase<int, int>(3, 4)]              // Types explicit - same as above
    [TestCase<double, double>(1.5, 2.5)]    // Different types
    public void MixedTestCases<T1, T2>(T1 a, T2 b)
    {
        Assert.That(a, Is.InstanceOf<T1>());
        Assert.That(b, Is.InstanceOf<T2>());
    }
    

    Benefits of Generic TestCase

    • Compile-time type checking: Errors are caught at compile time rather than runtime
    • Better IDE support: IntelliSense provides accurate parameter types
    • Clearer intent: The expected types are explicit in the attribute declaration
    • Refactoring safety: Type changes are caught by the compiler

    See Also

    • Test Attribute
    • Values Attribute
    • Range Attribute
    • Random Attribute
    • Platform Attribute
    • Ignore Attribute
    • Explicit Attribute
    • TestCaseSource Attribute
    • Edit this page
    In this article
    Back to top Generated by DocFX | Copyright (c) 2018- The NUnit Project - Licensed under CC BY-NC-SA 4.0