Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using [DependsOn] on tests in a parameterised test class #1570

Closed
skoging opened this issue Jan 15, 2025 · 6 comments · Fixed by #1581
Closed

Using [DependsOn] on tests in a parameterised test class #1570

skoging opened this issue Jan 15, 2025 · 6 comments · Fixed by #1581

Comments

@skoging
Copy link

skoging commented Jan 15, 2025

Version: 0.6.100

Combining [DependsOn] tests with class parameters is not currently working as I would expect.

The intention I had was to create a sequence of dependant tests, that can be parameterised as a whole. But this turned out not to be possible currently.

Expectation:

  • [DependsOn(nameof(Some_Test))] sets up a dependency on Some_Test with the same class argument value.
  • TestContext.Current.GetTests(nameof(Some_Test)) returns only the Some_Test case(s) with the same class argument value. Currently all cases of Some_Test is returned, so filtering on the class argument has to be done manually.
  • Using [MethodDataSource(nameof(TestCases))] with a yield return () => new SomeReferenceType() as the source for class argument values should also work (somehow)
    • Should all tests in the class for a specific argument get the same instance or not?
    • Reusing the same instance might not be necessary, as long as TestContext.Current.GetTests() returns the tests for the same class argument value. This might be easier to implement however, if the same instance is reused for the class.

Example of this not working as expected in the simple case with [Arguments(...)] on the class:

[Arguments("first")]
[Arguments("second")]
public class Repro(string testCase)
{
    [Test]
    public async Task Dependency()
    {
        await Assert.That(testCase).IsEqualTo(testCase);
        TestContext.Current!.ObjectBag["case"] = testCase;
    }

    [Test]
    [DependsOn(nameof(Dependency))]
    public async Task GetTests_Without_Filtering_On_TestClassArguments_Test()
    {
        var dependencyContext = TestContext.Current!
            .GetTests(nameof(Dependency))
            .First();


       // Fails with argument "second":
       //     Expected dependencyContext.ObjectBag["case"] to be equal to second but found first
        
        await Assert.That(dependencyContext.ObjectBag["case"]).IsEqualTo(testCase);
    }

    [Test]
    [DependsOn(nameof(Dependency))]
    public async Task GetTests_With_Filtering_On_TestClassArguments_Test()
    {
        var dependencyContext = TestContext.Current!
            .GetTests(nameof(Dependency))
            .Where(t => t.TestDetails.TestClassArguments.SequenceEqual(TestContext.Current!.TestDetails.TestClassArguments))
            .First();

        // Passes when `GetTests` is filtered based `TestClassArguments`
        
        await Assert.That(dependencyContext.ObjectBag["case"]).IsEqualTo(testCase);
    }
}

(This usage of SequenceEquals would fail in the case of a MethodDataSource returning unique instances of the values, as the references differ for each test)

Full test output

❯ dotnet run --no-build -- --treenode-filter "/*/*/Repro/*"

████████╗██╗   ██╗███╗   ██╗██╗████████╗
╚══██╔══╝██║   ██║████╗  ██║██║╚══██╔══╝
   ██║   ██║   ██║██╔██╗ ██║██║   ██║   
   ██║   ██║   ██║██║╚██╗██║██║   ██║   
   ██║   ╚██████╔╝██║ ╚████║██║   ██║   
   ╚═╝    ╚═════╝ ╚═╝  ╚═══╝╚═╝   ╚═╝   
   
   TUnit v0.6.100.0 | 64-bit | Darwin 24.1.0 Darwin Kernel Version 24.1.0: Thu Oct 10 21:03:15 PDT 2024; root:xnu-11215.41.3~2/RELEASE_ARM64_T6000 | osx-arm64 | .NET 9.0.0 | Microsoft Testing Platform v1.5.0
   
failed GetTests_Without_Filtering_On_TestClassArguments_Test (12ms)
  Expected dependancyContext.ObjectBag["case"] to be equal to second
  
  but found first
  
  at Assert.That(dependancyContext.ObjectBag["case"]).IsEqualTo(testCase)
    at TUnit.Assertions.AssertionBuilders.AssertionBuilder`1.ProcessAssertionsAsync()
    at TUnit.Assertions.AssertionBuilders.InvokableValueAssertionBuilder`1.AssertAndGet()
    at Tests.End2End.Repro.GetTests_Without_Filtering_On_TestClassArguments_Test() in Repro.cs:37
    at TUnit.Core.AsyncConvert.Convert(Func`1 action)
    at TUnit.Core.DiscoveredTest`1.ExecuteTest(CancellationToken cancellationToken)
    at TUnit.Engine.Helpers.Timings.Record(String name, TestContext context, Func`1 action)
    at TUnit.Engine.Services.TestInvoker.Invoke(DiscoveredTest discoveredTest, CancellationToken cancellationToken, List`1 cleanupExceptions)
    at TUnit.Engine.Services.TestInvoker.Invoke(DiscoveredTest discoveredTest, CancellationToken cancellationToken, List`1 cleanupExceptions)
    at TUnit.Engine.Services.SingleTestExecutor.ExecuteTestMethodWithTimeout(DiscoveredTest discoveredTest, CancellationToken cancellationToken, List`1 cleanupExceptions)
    at TUnit.Engine.Services.SingleTestExecutor.ExecuteWithCancellationTokens(DiscoveredTest discoveredTest, List`1 cleanupExceptions)
    at TUnit.Engine.Services.SingleTestExecutor.ExecuteWithRetries(DiscoveredTest discoveredTest, List`1 cleanupExceptions)
    at TUnit.Engine.Services.SingleTestExecutor.ExecuteWithRetries(DiscoveredTest discoveredTest, List`1 cleanupExceptions)
    at TUnit.Engine.Services.SingleTestExecutor.ExecuteTestInternalAsync(DiscoveredTest test, ITestExecutionFilter filter, ExecuteRequestContext context, Boolean isStartedAsDependencyForAnotherTest)
    at TUnit.Engine.Services.SingleTestExecutor.ExecuteTestInternalAsync(DiscoveredTest test, ITestExecutionFilter filter, ExecuteRequestContext context, Boolean isStartedAsDependencyForAnotherTest)
    at TUnit.Engine.Services.SingleTestExecutor.ExecuteTestInternalAsync(DiscoveredTest test, ITestExecutionFilter filter, ExecuteRequestContext context, Boolean isStartedAsDependencyForAnotherTest)
    at TUnit.Engine.Services.SingleTestExecutor.ExecuteTestInternalAsync(DiscoveredTest test, ITestExecutionFilter filter, ExecuteRequestContext context, Boolean isStartedAsDependencyForAnotherTest)
    at TUnit.Engine.Services.TestsExecutor.ProcessTest(DiscoveredTest test, ITestExecutionFilter filter, ExecuteRequestContext context, CancellationToken cancellationToken)

Test run summary: Failed! - /bin/Tests.End2End/debug/Tests.End2End.dll (net9.0|arm64)
  total: 6
  failed: 1
  succeeded: 5
  skipped: 0
  duration: 1s 115ms

When running the tests for only one of the arguments, both dependant tests for either argument fail with the same exception:

Cannot get unfinished tests - Did you mean to add a [DependsOn] attribute?
   at TUnit.Core.Extensions.TestContextExtensions.GetTests(TestContext context, String testName, Type[] parameterTypes)
   at TUnit.Core.Extensions.TestContextExtensions.GetTests(TestContext context, String testName)
   at Tests.Repro.GetTests_With_Filtering_On_TestClassArguments_Test() in {...}/Repro.cs:line 36
   at TUnit.Core.AsyncConvert.Convert(Func`1 action)
   at TUnit.Core.DiscoveredTest`1.ExecuteTest(CancellationToken cancellationToken)
   at TUnit.Engine.Helpers.Timings.Record(String name, TestContext context, Func`1 action)
   at TUnit.Engine.Services.TestInvoker.Invoke(DiscoveredTest discoveredTest, CancellationToken cancellationToken, List`1 cleanupExceptions)
   at TUnit.Engine.Services.TestInvoker.Invoke(DiscoveredTest discoveredTest, CancellationToken cancellationToken, List`1 cleanupExceptions)
   at TUnit.Engine.Services.SingleTestExecutor.ExecuteTestMethodWithTimeout(DiscoveredTest discoveredTest, CancellationToken cancellationToken, List`1 cleanupExceptions)
   at TUnit.Engine.Services.SingleTestExecutor.ExecuteWithCancellationTokens(DiscoveredTest discoveredTest, List`1 cleanupExceptions)
   at TUnit.Engine.Services.SingleTestExecutor.ExecuteWithRetries(DiscoveredTest discoveredTest, List`1 cleanupExceptions)
   at TUnit.Engine.Services.SingleTestExecutor.ExecuteWithRetries(DiscoveredTest discoveredTest, List`1 cleanupExceptions)
   at TUnit.Engine.Services.SingleTestExecutor.ExecuteTestInternalAsync(DiscoveredTest test, ITestExecutionFilter filter, ExecuteRequestContext context, Boolean isStartedAsDependencyForAnotherTest)
   at TUnit.Engine.Services.SingleTestExecutor.ExecuteTestInternalAsync(DiscoveredTest test, ITestExecutionFilter filter, ExecuteRequestContext context, Boolean isStartedAsDependencyForAnotherTest)
   at TUnit.Engine.Services.SingleTestExecutor.ExecuteTestInternalAsync(DiscoveredTest test, ITestExecutionFilter filter, ExecuteRequestContext context, Boolean isStartedAsDependencyForAnotherTest)
   at TUnit.Engine.Services.SingleTestExecutor.ExecuteTestInternalAsync(DiscoveredTest test, ITestExecutionFilter filter, ExecuteRequestContext context, Boolean isStartedAsDependencyForAnotherTest)
   at TUnit.Engine.Services.TestsExecutor.ProcessTest(DiscoveredTest test, ITestExecutionFilter filter, ExecuteRequestContext context, CancellationToken cancellationToken)

It looks like there is a related problem somewhere that results in the [DependsOn(...)] attribute not ensuring that the "dependency" is finished first unless all variants of the "dependency" test is included in the run. If I manually include the "other" variant(s) of the dependency, the tests are run in a valid order.

@thomhurst
Copy link
Owner

Interesting use case! I hadn't considered this scenario. Love that people can get a bit creative with the data inputs 😄

My first thought it something like this:

[DependsOn(nameof(Dependency), Matching = MatchType.ClassArguments)]

What do you think?

@skoging
Copy link
Author

skoging commented Jan 15, 2025

The [DependsOn] logic is honestly incredibly powerful!

Is there any reason why DependsOn shouldn't be scoped to the class arguments by default? I understand that the GetTests changes I asked for may be tricky, but that is in many ways a separate issue that can be solved in a number of possible ways without.

As is, using [DependsOn] inside a class with multiple [Arguments] or a [MethodDataSource] with multiple values does not work consistently. It seems to only work if all tests are included in the run, or if the dependency test with the "first" class argument is manually included. If needed I can come back with a more detailed reproduction for this behaviour tomorrow.

@thomhurst
Copy link
Owner

The [DependsOn] logic is honestly incredibly powerful!

Is there any reason why DependsOn shouldn't be scoped to the class arguments by default? I understand that the GetTests changes I asked for may be tricky, but that is in many ways a separate issue that can be solved in a number of possible ways without.

As is, using [DependsOn] inside a class with multiple [Arguments] or a [MethodDataSource] with multiple values does not work consistently. It seems to only work if all tests are included in the run, or if the dependency test with the "first" class argument is manually included. If needed I can come back with a more detailed reproduction for this behaviour tomorrow.

Is there any reason why DependsOn shouldn't be scoped to the class arguments by default?

I'm trying to think of downsides but I think you're right. That data will filter down to all tests within it, so it should be fine.
I initially was thinking you might get tests with different method data inputs, and I guess I just didn't consider this.

As is, using [DependsOn] inside a class with multiple [Arguments] or a [MethodDataSource] with multiple values does not work consistently. It seems to only work if all tests are included in the run, or if the dependency test with the "first" class argument is manually included. If needed I can come back with a more detailed reproduction for this behaviour tomorrow.

Not sure I quite understand what you mean. A repro would probably help me 😄

@skoging
Copy link
Author

skoging commented Jan 16, 2025

Repro code (same as above, with a custom `[ArgumentProperty]` to be able to filter based on argument)

public class ArgumentPropertyAttribute(string name = "Argument") : Attribute, ITestDiscoveryEventReceiver
{
    public void OnTestDiscovery(DiscoveredTestContext discoveredTestContext)
    {
        var value = string.Join(", ", discoveredTestContext.TestDetails.TestClassArguments);
        discoveredTestContext.AddProperty(name, value);
    }
}

[Arguments("first")]
[Arguments("second")]
[ArgumentProperty]
public class Repro(string testCase)
{
    [Test]
    public async Task Dependency()
    {
        await Assert.That(testCase).IsEqualTo(testCase);
        TestContext.Current!.ObjectBag["case"] = testCase;
    }

    [Test]
    [DependsOn(nameof(Dependency))]
    public async Task GetTests_Without_Filtering_On_TestClassArguments_Test()
    {
        var dependencyContext = TestContext.Current!
            .GetTests(nameof(Dependency))
            .First();


        // Fails with argument "second":
        //     Expected dependencyContext.ObjectBag["case"] to be equal to second but found first
        
        await Assert.That(dependencyContext.ObjectBag["case"]).IsEqualTo(testCase);
    }

    [Test]
    [DependsOn(nameof(Dependency))]
    public async Task GetTests_With_Filtering_On_TestClassArguments_Test()
    {
        var dependencyContext = TestContext.Current!
            .GetTests(nameof(Dependency))
            .Where(t => t.TestDetails.TestClassArguments.SequenceEqual(TestContext.Current!.TestDetails.TestClassArguments))
            .First();

        // Passes when `GetTests` is filtered based `TestClassArguments`
        
        await Assert.That(dependencyContext.ObjectBag["case"]).IsEqualTo(testCase);
    }
}

Output with/without filter on argument value

Output when running the GetTests_With_Filtering_On_TestClassArguments_Test without argument filter:

❯ dotnet run --no-build --treenode-filter "/*/*/Repro/GetTests_With_Filtering_On_TestClassArguments_Test" --output Detailed 

████████╗██╗   ██╗███╗   ██╗██╗████████╗
╚══██╔══╝██║   ██║████╗  ██║██║╚══██╔══╝
   ██║   ██║   ██║██╔██╗ ██║██║   ██║   
   ██║   ██║   ██║██║╚██╗██║██║   ██║   
   ██║   ╚██████╔╝██║ ╚████║██║   ██║   
   ╚═╝    ╚═════╝ ╚═╝  ╚═══╝╚═╝   ╚═╝   
   
   TUnit v0.6.100.0 | 64-bit | Darwin 24.1.0 Darwin Kernel Version 24.1.0: Thu Oct 10 21:03:15 PDT 2024; root:xnu-11215.41.3~2/RELEASE_ARM64_T6000 | osx-arm64 | .NET 9.0.0 | Microsoft Testing Platform v1.5.0
   
passed Dependency (12ms)
passed Dependency (16ms)
passed GetTests_With_Filtering_On_TestClassArguments_Test (1ms)
passed GetTests_With_Filtering_On_TestClassArguments_Test (3ms)

Test run summary: Passed! - /Users/tore-adept/repos/adept/Clavis/artifacts/bin/Tests.End2End/debug/Tests.End2End.dll (net9.0|arm64)
  total: 4
  failed: 0
  succeeded: 4
  skipped: 0
  duration: 96ms

Output when running the GetTests_With_Filtering_On_TestClassArguments_Test with argument filter:

"first":

❯ dotnet run --no-build --treenode-filter "/*/*/Repro/GetTests_With_Filtering_On_TestClassArguments_Test[Argument=first]" --output Detailed

████████╗██╗   ██╗███╗   ██╗██╗████████╗
╚══██╔══╝██║   ██║████╗  ██║██║╚══██╔══╝
   ██║   ██║   ██║██╔██╗ ██║██║   ██║   
   ██║   ██║   ██║██║╚██╗██║██║   ██║   
   ██║   ╚██████╔╝██║ ╚████║██║   ██║   
   ╚═╝    ╚═════╝ ╚═╝  ╚═══╝╚═╝   ╚═╝   
   
   TUnit v0.6.100.0 | 64-bit | Darwin 24.1.0 Darwin Kernel Version 24.1.0: Thu Oct 10 21:03:15 PDT 2024; root:xnu-11215.41.3~2/RELEASE_ARM64_T6000 | osx-arm64 | .NET 9.0.0 | Microsoft Testing Platform v1.5.0
   
passed Dependency (16ms)
failed GetTests_With_Filtering_On_TestClassArguments_Test (10ms)
  System.Exception: Cannot get unfinished tests - Did you mean to add a [DependsOn] attribute?
    at TUnit.Core.Extensions.TestContextExtensions.GetTests(TestContext context, String testName, Type[] parameterTypes)
    at TUnit.Core.Extensions.TestContextExtensions.GetTests(TestContext context, String testName)
    at Tests.End2End.Repro.GetTests_With_Filtering_On_TestClassArguments_Test() in Repro.cs:46
    at TUnit.Core.AsyncConvert.Convert(Func`1 action)
    at TUnit.Core.DiscoveredTest`1.ExecuteTest(CancellationToken cancellationToken)
    at TUnit.Engine.Helpers.Timings.Record(String name, TestContext context, Func`1 action)
    at TUnit.Engine.Services.TestInvoker.Invoke(DiscoveredTest discoveredTest, CancellationToken cancellationToken, List`1 cleanupExceptions)
    at TUnit.Engine.Services.TestInvoker.Invoke(DiscoveredTest discoveredTest, CancellationToken cancellationToken, List`1 cleanupExceptions)
    at TUnit.Engine.Services.SingleTestExecutor.ExecuteTestMethodWithTimeout(DiscoveredTest discoveredTest, CancellationToken cancellationToken, List`1 cleanupExceptions)
    at TUnit.Engine.Services.SingleTestExecutor.ExecuteWithCancellationTokens(DiscoveredTest discoveredTest, List`1 cleanupExceptions)
    at TUnit.Engine.Services.SingleTestExecutor.ExecuteWithRetries(DiscoveredTest discoveredTest, List`1 cleanupExceptions)
    at TUnit.Engine.Services.SingleTestExecutor.ExecuteWithRetries(DiscoveredTest discoveredTest, List`1 cleanupExceptions)
    at TUnit.Engine.Services.SingleTestExecutor.ExecuteTestInternalAsync(DiscoveredTest test, ITestExecutionFilter filter, ExecuteRequestContext context, Boolean isStartedAsDependencyForAnotherTest)
    at TUnit.Engine.Services.SingleTestExecutor.ExecuteTestInternalAsync(DiscoveredTest test, ITestExecutionFilter filter, ExecuteRequestContext context, Boolean isStartedAsDependencyForAnotherTest)
    at TUnit.Engine.Services.SingleTestExecutor.ExecuteTestInternalAsync(DiscoveredTest test, ITestExecutionFilter filter, ExecuteRequestContext context, Boolean isStartedAsDependencyForAnotherTest)
    at TUnit.Engine.Services.SingleTestExecutor.ExecuteTestInternalAsync(DiscoveredTest test, ITestExecutionFilter filter, ExecuteRequestContext context, Boolean isStartedAsDependencyForAnotherTest)

Test run summary: Failed! - /Users/tore-adept/repos/adept/Clavis/artifacts/bin/Tests.End2End/debug/Tests.End2End.dll (net9.0|arm64)
  total: 2
  failed: 1
  succeeded: 1
  skipped: 0
  duration: 107ms

"second":

❯ dotnet run --no-build --treenode-filter "/*/*/Repro/GetTests_With_Filtering_On_TestClassArguments_Test[Argument=second]" --output Detailed

████████╗██╗   ██╗███╗   ██╗██╗████████╗
╚══██╔══╝██║   ██║████╗  ██║██║╚══██╔══╝
   ██║   ██║   ██║██╔██╗ ██║██║   ██║   
   ██║   ██║   ██║██║╚██╗██║██║   ██║   
   ██║   ╚██████╔╝██║ ╚████║██║   ██║   
   ╚═╝    ╚═════╝ ╚═╝  ╚═══╝╚═╝   ╚═╝   
   
   TUnit v0.6.100.0 | 64-bit | Darwin 24.1.0 Darwin Kernel Version 24.1.0: Thu Oct 10 21:03:15 PDT 2024; root:xnu-11215.41.3~2/RELEASE_ARM64_T6000 | osx-arm64 | .NET 9.0.0 | Microsoft Testing Platform v1.5.0
   
passed Dependency (15ms)
failed GetTests_With_Filtering_On_TestClassArguments_Test (9ms)
  System.Exception: Cannot get unfinished tests - Did you mean to add a [DependsOn] attribute?
    at TUnit.Core.Extensions.TestContextExtensions.GetTests(TestContext context, String testName, Type[] parameterTypes)
    at TUnit.Core.Extensions.TestContextExtensions.GetTests(TestContext context, String testName)
    at Tests.End2End.Repro.GetTests_With_Filtering_On_TestClassArguments_Test() in Repro.cs:46
    at TUnit.Core.AsyncConvert.Convert(Func`1 action)
    at TUnit.Core.DiscoveredTest`1.ExecuteTest(CancellationToken cancellationToken)
    at TUnit.Engine.Helpers.Timings.Record(String name, TestContext context, Func`1 action)
    at TUnit.Engine.Services.TestInvoker.Invoke(DiscoveredTest discoveredTest, CancellationToken cancellationToken, List`1 cleanupExceptions)
    at TUnit.Engine.Services.TestInvoker.Invoke(DiscoveredTest discoveredTest, CancellationToken cancellationToken, List`1 cleanupExceptions)
    at TUnit.Engine.Services.SingleTestExecutor.ExecuteTestMethodWithTimeout(DiscoveredTest discoveredTest, CancellationToken cancellationToken, List`1 cleanupExceptions)
    at TUnit.Engine.Services.SingleTestExecutor.ExecuteWithCancellationTokens(DiscoveredTest discoveredTest, List`1 cleanupExceptions)
    at TUnit.Engine.Services.SingleTestExecutor.ExecuteWithRetries(DiscoveredTest discoveredTest, List`1 cleanupExceptions)
    at TUnit.Engine.Services.SingleTestExecutor.ExecuteWithRetries(DiscoveredTest discoveredTest, List`1 cleanupExceptions)
    at TUnit.Engine.Services.SingleTestExecutor.ExecuteTestInternalAsync(DiscoveredTest test, ITestExecutionFilter filter, ExecuteRequestContext context, Boolean isStartedAsDependencyForAnotherTest)
    at TUnit.Engine.Services.SingleTestExecutor.ExecuteTestInternalAsync(DiscoveredTest test, ITestExecutionFilter filter, ExecuteRequestContext context, Boolean isStartedAsDependencyForAnotherTest)
    at TUnit.Engine.Services.SingleTestExecutor.ExecuteTestInternalAsync(DiscoveredTest test, ITestExecutionFilter filter, ExecuteRequestContext context, Boolean isStartedAsDependencyForAnotherTest)
    at TUnit.Engine.Services.SingleTestExecutor.ExecuteTestInternalAsync(DiscoveredTest test, ITestExecutionFilter filter, ExecuteRequestContext context, Boolean isStartedAsDependencyForAnotherTest)

Test run summary: Failed! - /Users/tore-adept/repos/adept/Clavis/artifacts/bin/Tests.End2End/debug/Tests.End2End.dll (net9.0|arm64)
  total: 2
  failed: 1
  succeeded: 1
  skipped: 0
  duration: 104ms

The custom [ArgumentProperty] attribute is only there to enable filtering on the argument value as a part of --treenode-filter. The same behaviour can be reproduced in IDEs (atleast in Rider where I tested) by just selecting which test(s) to run.

When debugging in rider it appears that [DependsOn] does actually ensure that only the case with the same argument value is triggered. This causes an issue with GetTests, however. It does not filter the tests to the same class argument value, and since the testcase for other values has not been triggered an exception is thrown: "Cannot get unfinished tests - Did you mean to add a [DependsOn] attribute?".

I conclusion, I think [DependsOn] actually does work as i expect in parameterised test classes. The dependency is correctly scoped to cases with the same class argument values. The actual issue is GetTests which currently requires all cases of the named test to be complete, otherwise it throws an exception complaining of missing [DependsOn]. This issue is hidden if all cases of the test dependency is included in the run, and only shows up when specific test(s) are run.

@skoging
Copy link
Author

skoging commented Jan 16, 2025

Interesting use case! I hadn't considered this scenario. Love that people can get a bit creative with the data inputs 😄

My first thought it something like this:

[DependsOn(nameof(Dependency), Matching = MatchType.ClassArguments)]

What do you think?

Based on further debugging, this seems to not be necessary. The current behaviour seems to be to match class argument values for [DependsOn(...)] tests.

The actual change that I would be something like an overload of GetTests to enable filtering based on class argument values:

[Arguments("first")]
[Arguments("second")]
public class Repro(string arg)
{
    [Test]
    [DependsOn(nameof(Dependency))]
    public async Task GetTests_Test()
    {
       TestContext.Current .GetTests(nameof(Dependency), classArguments: [arg]);
    }

    [Test]
    public async Task Dependency() {}
}

Another possibility is to just have this filtering as the default behaviour of GetTests:

public static TestContext[] GetTests(this TestContext context, string testName, Type[] parameterTypes)
{
    var tests = context.GetService<ITestFinder>().GetTestsByNameAndParameters(
        testName: testName, 
        methodParameterTypes: parameterTypes, 
        classType: context.TestDetails.ClassType, 
        classParameterTypes: context.TestDetails.TestClassParameterTypes,
+        classArguments: context.TestDetails.TestClassArguments);

    if (tests.Any(x => x.TestTask?.IsCompleted is not true))
    {
        throw new Exception("Cannot get unfinished tests - Did you mean to add a [DependsOn] attribute?");
    }
    
    return tests;
}

This would make GetTests just work (tm) when combined with class arguments.
(There may be cases where you want to get the tests with other class argument values, but this would also require a change to [DependsOn] to be able to specify which class argument value to depend on for the test.)

@thomhurst
Copy link
Owner

If you'd like to give 0.6.117 a try 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants