Conversation Summary
- You asked whether it’s possible to inject dependencies directly into F# xUnit test method arguments, and if not, whether it could be implemented.
- I explained why xUnit doesn’t natively support this, especially with F# static methods, but that it’s possible with custom DataAttributes.
- You asked about the impact on performance and test discovery/naming in test runners when using a custom DataAttribute for DI.
- I described the expected behavior, performance, and test runner appearance.
- You requested example code for such a custom attribute in C# (adaptable to F#).
- You asked for clarification on why F# attributes are considered more limited than C#’s.
Expanded Summary: Steps to Get to the Custom DataAttribute Code
using System;
using System.Collections.Generic;
using System.Reflection;
using Xunit.Sdk;
public class InjectFromServicesDataAttribute : DataAttribute
{
private static IServiceProvider GlobalServiceProvider = null;
// For demo only: Normally set this up via test class/fixture setup, not directly!
public static void SetServiceProvider(IServiceProvider sp) => GlobalServiceProvider = sp;
public override IEnumerable<object[]> GetData(MethodInfo testMethod)
{
if (GlobalServiceProvider == null)
throw new InvalidOperationException("GlobalServiceProvider not set.");
var parameters = testMethod.GetParameters();
var values = new object[parameters.Length];
for (int i = 0; i < parameters.Length; i++)
{
var dep = GlobalServiceProvider.GetService(parameters[i].ParameterType);
if (dep == null)
throw new InvalidOperationException($"Cannot resolve {parameters[i].ParameterType}");
values[i] = dep;
}
yield return values;
}
public override string GetDisplayName(MethodInfo methodInfo, object[] data)
{
var paramNames = methodInfo.GetParameters();
var dataTypes = string.Join(", ",
data != null
? data.Select(d => d?.GetType().Name ?? "null")
: paramNames.Select(p => p.ParameterType.Name)
);
return $"{methodInfo.Name}({dataTypes})";
}
}
I want to expand on this in the following ways:
- Enable passing primitive inline data into the attribute so that it can be set on primitive parameters (e.g. parents 1-3 are complex types, injected, parameters 4-5 are primitive and should be sourced from attribute)
- For each test we should start a new Scope on the DI container
- In the display name generation we should only set primitive data as arguments and skip any injected ones, unless they have an overloaded toString which will provide a sensible value.
I want this added into a new shared F# test project so it can be referenced by the F# tests.
Conversation Summary
Expanded Summary: Steps to Get to the Custom DataAttribute Code
Understand the need:
Constraints:
[Theory]test parameters.Design a solution:
[Theory]test, resolves parameters from a DI container (typically IServiceProvider).GetService.GetDisplayNamefor the test method so it appears meaningfully in test runners.Performance/Usability considerations:
Code outline and example usage:
InjectFromServicesDataAttribute).[Theory, InjectFromServicesData]and provide DI-required parameters.F# and C# differences:
I want to expand on this in the following ways:
I want this added into a new shared F# test project so it can be referenced by the F# tests.