-
-
Notifications
You must be signed in to change notification settings - Fork 47
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
Comments
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:
What do you think? |
The Is there any reason why As is, using |
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.
Not sure I quite understand what you mean. A repro would probably help me 😄 |
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 ❯ 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 "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 When debugging in rider it appears that I conclusion, I think |
Based on further debugging, this seems to not be necessary. The current behaviour seems to be to match class argument values for The actual change that I would be something like an overload of [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 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 |
If you'd like to give |
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 onSome_Test
with the same class argument value.TestContext.Current.GetTests(nameof(Some_Test))
returns only theSome_Test
case(s) with the same class argument value. Currently all cases ofSome_Test
is returned, so filtering on the class argument has to be done manually.[MethodDataSource(nameof(TestCases))]
with ayield return () => new SomeReferenceType()
as the source for class argument values should also work (somehow)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:(This usage of
SequenceEquals
would fail in the case of aMethodDataSource
returning unique instances of the values, as the references differ for each test)Full test output
When running the tests for only one of the arguments, both dependant tests for either argument fail with the same exception:
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.The text was updated successfully, but these errors were encountered: