Skip to content

Commit de83ec8

Browse files
author
DMarkovkin
committed
use instance method or prop as ServiceProviderMemberData source
1 parent 31ec6d2 commit de83ec8

4 files changed

Lines changed: 65 additions & 38 deletions

File tree

docs/xunit.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,21 @@ public class EnvironmentInitializer : IFrameworkInitializer
3838
```
3939

4040
**Parameterized tests with data from the IServiceProvider:**
41-
Create a static method that accepts IServiceProvider and returns IEnumerable<object>. Pass that method to ServiceProviderMemberDataAttribute to use it as data source for Theory.
41+
Create a instance method or property that returns IEnumerable<object>. Pass that method to ServiceProviderMemberDataAttribute to use it as data source for Theory.
4242

4343
```
4444
[Bss.Testing.Xunit.Sdk.Theory]
4545
[ServiceProviderMemberData(nameof(GetMemberData))]
4646
public void GetDataFromServiceProvider(FullSecurityRole role) => Assert.NotEmpty(role.Name);
4747
48-
protected static IEnumerable<object> GetMemberData(IServiceProvider serviceProvider) =>
49-
serviceProvider.GetRequiredService<ISecurityRoleSource>().SecurityRoles.Select(x => new [] { x });
48+
protected IEnumerable<object> GetMemberData() =>
49+
this.ServiceProvider.GetRequiredService<ISecurityRoleSource>().SecurityRoles.Select(x => new [] { x });
50+
```
51+
52+
```
53+
[Bss.Testing.Xunit.Sdk.Theory]
54+
[ServiceProviderMemberData(nameof(GetMemberData))]
55+
public void GetDataFromServiceProvider(FullSecurityRole role) => Assert.NotEmpty(role.Name);
56+
57+
protected IEnumerable<object> GetMemberData => this.ServiceProvider.GetRequiredService<ISecurityRoleSource>().SecurityRoles.Select(x => new [] { x });
5058
```

xunit/src/Bss.Testing.Xunit/Sdk/ServiceProviderMemberDataAttribute.cs

Lines changed: 47 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,86 +5,101 @@
55

66
using System.Reflection;
77

8+
using Microsoft.Extensions.DependencyInjection;
9+
810
namespace Bss.Testing.Xunit.Sdk;
911

1012
[DataDiscoverer("Bss.Testing.Xunit.Sdk.ServiceProviderMemberDataDiscoverer", "Bss.Testing.Xunit")]
1113
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
12-
public class ServiceProviderMemberDataAttribute(string methodName) : DataAttribute
14+
public class ServiceProviderMemberDataAttribute(string methodOrPropertyName) : DataAttribute
1315
{
14-
public Type? MemberType { get; set; }
16+
public Type MemberType { get; set; }
1517

16-
string MemberName { get; set; } = methodName;
18+
string MemberName { get; set; } = methodOrPropertyName;
1719

18-
public override IEnumerable<object[]>? GetData(MethodInfo testMethod) => null;
20+
public override IEnumerable<object[]> GetData(MethodInfo testMethod) => null;
1921

20-
public IEnumerable<object[]>? GetData(MethodInfo testMethod, IServiceProvider? serviceProvider)
22+
public IEnumerable<object[]> GetData(MethodInfo testMethod, IServiceProvider serviceProvider)
2123
{
2224
var type = this.MemberType ?? testMethod.DeclaringType;
23-
if (type == null)
24-
{
25-
throw new ArgumentException(
26-
string.Format(
27-
CultureInfo.CurrentCulture,
28-
"Could not find type {0}",
29-
type?.FullName)
30-
);
31-
}
25+
var accessor = this.GetMethodAccessor(type, serviceProvider)
26+
?? this.GetPropertyAccessor(type, serviceProvider);
3227

33-
var accessor = this.GetMethodAccessor(type, serviceProvider);
3428
if (accessor == null)
3529
{
3630
throw new ArgumentException(
3731
string.Format(
3832
CultureInfo.CurrentCulture,
39-
"Could not find public static method named '{0}' on {1}{2}",
33+
"Could not find parameterless method or property named '{0}' on {1} provided in ServiceProviderMemberDataAttribute",
4034
this.MemberName,
41-
type?.FullName,
42-
" with parameter types: IServiceProvider")
35+
type?.FullName)
4336
);
4437
}
4538

4639
var obj = accessor();
4740
if (obj == null)
4841
{
49-
return (IEnumerable<object[]>) Array.Empty<object>();
42+
return null;
5043
}
5144

5245
if (obj is not IEnumerable dataItems)
5346
{
54-
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "Method {0} on {1} did not return IEnumerable", this.MemberName, type?.FullName));
47+
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "Method/property {0} on {1} did not return IEnumerable", this.MemberName, type?.FullName));
5548
}
5649

57-
return dataItems.Cast<object>().Select(item => this.ConvertDataItem(testMethod, item))!;
50+
return dataItems.Cast<object>().Select(item => this.ConvertDataItem(testMethod, item));
5851
}
5952

60-
protected Func<object>? GetMethodAccessor(Type type, IServiceProvider? serviceProvider)
53+
private Func<object?>? GetMethodAccessor(Type type, IServiceProvider serviceProvider)
6154
{
6255
MethodInfo? methodInfo = null;
6356
for (var reflectionType = type; reflectionType != null; reflectionType = reflectionType.GetTypeInfo().BaseType)
6457
{
65-
var runtimeMethodsWithGivenName = reflectionType.GetRuntimeMethods()
66-
.Where(m => m.Name == this.MemberName)
67-
.ToArray();
58+
methodInfo = reflectionType
59+
.GetRuntimeMethods()
60+
.FirstOrDefault(m => m.Name == this.MemberName);
61+
if (methodInfo != null)
62+
{
63+
break;
64+
}
65+
}
66+
67+
if (methodInfo == null)
68+
{
69+
return null;
70+
}
6871

69-
methodInfo = runtimeMethodsWithGivenName
70-
.FirstOrDefault(m => m.GetParameters()
71-
.Count(x => x.ParameterType.IsAssignableTo(typeof(IServiceProvider))) == 1);
72+
var @object = ActivatorUtilities.CreateInstance(serviceProvider, type);
7273

73-
if (methodInfo != null)
74+
return () => methodInfo.Invoke(@object, null);
75+
}
76+
77+
private Func<object?>? GetPropertyAccessor(Type type, IServiceProvider serviceProvider)
78+
{
79+
PropertyInfo? propertyInfo = null;
80+
for (var reflectionType = type; reflectionType != null; reflectionType = reflectionType.GetTypeInfo().BaseType)
81+
{
82+
propertyInfo = reflectionType
83+
.GetProperties()
84+
.FirstOrDefault(m => m.Name == this.MemberName);
85+
86+
if (propertyInfo != null)
7487
{
7588
break;
7689
}
7790
}
7891

79-
if (methodInfo == null || !methodInfo.IsStatic)
92+
if (propertyInfo == null)
8093
{
8194
return null;
8295
}
8396

84-
return () => methodInfo.Invoke(null, [serviceProvider])!;
97+
var @object = ActivatorUtilities.CreateInstance(serviceProvider, type);
98+
99+
return () => propertyInfo.GetValue(@object);
85100
}
86101

87-
protected object[]? ConvertDataItem(MethodInfo testMethod, object? item)
102+
private object[]? ConvertDataItem(MethodInfo testMethod, object? item)
88103
{
89104
if (item == null)
90105
{

xunit/src/Bss.Testing.Xunit/Sdk/ServiceProviderMemberDataDiscoverer.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,16 @@ namespace Bss.Testing.Xunit.Sdk;
66
public class ServiceProviderMemberDataDiscoverer : IDataDiscoverer
77
{
88
public IEnumerable<object[]> GetData(IAttributeInfo dataAttribute, IMethodInfo testMethod) =>
9-
throw new ArgumentException($"ServiceProviderMemberDataDiscoverer cannot be used as discoverer for any *DataAttribute other than ServiceProviderMemberDataAttribute.");
9+
throw new ArgumentException("ServiceProviderMemberDataDiscoverer cannot be used as discoverer for any *DataAttribute other than ServiceProviderMemberDataAttribute.");
1010

1111
public IEnumerable<object[]>? GetData(IAttributeInfo dataAttribute, IMethodInfo testMethod, IServiceProvider? serviceProvider)
1212
{
13+
if (serviceProvider == null)
14+
{
15+
throw new ArgumentException($"ServiceProvider cannot be null for {nameof(dataAttribute)}");
16+
}
17+
18+
1319
if (dataAttribute is not IReflectionAttributeInfo reflectionDataAttribute
1420
|| testMethod is not IReflectionMethodInfo reflectionTestMethod)
1521
{

xunit/src/Directory.Build.props

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010

1111
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
1212
<GenerateDocumentationFile>false</GenerateDocumentationFile>
13-
14-
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
1513
</PropertyGroup>
1614

1715
<PropertyGroup Condition="'$(Configuration)'=='Release'">

0 commit comments

Comments
 (0)