Skip to content

Add generic ConditionAttribute that evaluates static Type+member like xUnit's [ConditionalFact] #9070

Description

@Evangelink

Summary

Add a generic ConditionAttribute (likely a [ConditionalFact]/[ConditionalDataRow] equivalent) that defers the boolean condition evaluation to a static member referenced by Type + member name, mirroring xUnit.net's ConditionalFactAttribute/ConditionalTheoryAttribute (Microsoft.DotNet.XUnitExtensions).

Motivation

While migrating dotnet/sdk tests from xUnit to MSTest (dotnet/sdk#54731), many tests use:

[ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))]
public void InvalidAdvance_Large() { ... }

This pattern is extremely common in dotnet/runtime, dotnet/sdk, dotnet/aspnetcore, etc. It allows test authors to skip a test when an arbitrary static bool property/field/method returns false -- without having to write a new ConditionBaseAttribute subclass each time.

MSTest already ships specific ConditionBaseAttribute subclasses like [OSCondition], but there is no generic facility for "skip if Type.Member returns false". Today every project has to define its own one-off ConditionBaseAttribute (e.g. Is64BitProcessConditionAttribute, IsBigEndianConditionAttribute, IsLinuxBionicConditionAttribute, ...) which is boilerplate-heavy.

Proposed shape

Match the dotnet/runtime / xUnit.net naming so migration is mechanical:

[ConditionalTest(typeof(Environment), nameof(Environment.Is64BitProcess))]
[TestMethod]
public void InvalidAdvance_Large() { ... }

// Multiple members → AND together
[ConditionalTest(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser), nameof(PlatformDetection.IsThreadingSupported))]
public void HttpClient_Works() { ... }

Where each referenced member can be:

  • public static bool Property { get; }
  • public static readonly bool Field
  • public static bool Method()

Bonus: an [ConditionalIgnore(...)] flavor with the inverse semantics (skip when the member returns true), for symmetry with ConditionMode.Exclude.

Workaround today

Hand-roll a ConditionBaseAttribute per condition (verbose, hard to discover):

internal sealed class Is64BitProcessConditionAttribute : ConditionBaseAttribute
{
    public Is64BitProcessConditionAttribute() : base(ConditionMode.Include)
    {
        IgnoreMessage = ""This test requires a 64-bit process."";
    }
    public override string GroupName => nameof(Is64BitProcessConditionAttribute);
    public override bool IsConditionMet => Environment.Is64BitProcess;
}

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions