Skip to content

Commit ddd25c5

Browse files
Improved message when [ScenarioDependencies] can't be found or has an incorrect return type (Reqnroll.Microsoft.Extensions.DependencyInjection) (#494)
* Start: improve message when no servicecollection return * message partly to exception * improve MissingScenarioDependenciesException * cleanup * changelog * improved message: public or non-public
1 parent 5cac594 commit ddd25c5

File tree

5 files changed

+145
-7
lines changed

5 files changed

+145
-7
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# [vNext]
22

33
## Improvements:
4+
* Microsoft.Extensions.DependencyInjection.ReqnrollPlugin: Improved message when [ScenarioDependencies] can't be found or has an incorrect return type (#494)
45

56
## Bug fixes:
67
* Fix: Microsoft.Extensions.DependencyInjection.ReqnrollPlugin, the plugin was only searching for [ScenarioDependencies] in assemblies with step definitions (#477)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using System;
2+
3+
namespace Reqnroll.Microsoft.Extensions.DependencyInjection;
4+
5+
[Serializable]
6+
public class InvalidScenarioDependenciesException(string reason) : ReqnrollException("[ScenarioDependencies] should return IServiceCollection but " + reason)
7+
{
8+
public override string HelpLink { get; set; } = "https://go.reqnroll.net/doc-msdi";
9+
}
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,32 @@
11
using System;
2+
using System.Collections.Generic;
23

34
namespace Reqnroll.Microsoft.Extensions.DependencyInjection
45
{
56
[Serializable]
67
public class MissingScenarioDependenciesException : ReqnrollException
78
{
89
public MissingScenarioDependenciesException()
9-
: base("No method marked with [ScenarioDependencies] attribute found.")
10+
: this([])
1011
{
11-
HelpLink = "https://go.reqnroll.net/doc-msdi";
1212
}
13+
14+
public MissingScenarioDependenciesException(IList<string> assemblyNames)
15+
: base(CreateMessage(assemblyNames))
16+
{
17+
18+
}
19+
20+
private static string CreateMessage(IList<string> assemblyNames)
21+
{
22+
var message = "No method marked with [ScenarioDependencies] attribute found. It should be a (public or non-public) static method.";
23+
if (assemblyNames.Count > 0)
24+
{
25+
message += $" Scanned assemblies: {string.Join(", ", assemblyNames)}.";
26+
}
27+
return message;
28+
}
29+
30+
public override string HelpLink { get; set; } = "https://go.reqnroll.net/doc-msdi";
1331
}
1432
}

Plugins/Reqnroll.Microsoft.Extensions.DependencyInjection.ReqnrollPlugin/ServiceCollectionFinder.cs

+20-4
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public ServiceCollectionFinder(ITestRunnerManager testRunnerManager)
2828
{
2929
foreach (var type in assembly.GetTypes())
3030
{
31-
foreach (var methodInfo in type.GetMethods(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public))
31+
foreach (MethodInfo methodInfo in type.GetMethods(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public))
3232
{
3333
var scenarioDependenciesAttribute = (ScenarioDependenciesAttribute)Attribute.GetCustomAttribute(methodInfo, typeof(ScenarioDependenciesAttribute));
3434

@@ -44,12 +44,28 @@ public ServiceCollectionFinder(ITestRunnerManager testRunnerManager)
4444
}
4545
}
4646
}
47-
throw new MissingScenarioDependenciesException();
47+
var assemblyNames = assemblies.Select(a => a.GetName().Name).ToList();
48+
throw new MissingScenarioDependenciesException(assemblyNames);
4849
}
4950

50-
private static IServiceCollection GetServiceCollection(MethodBase methodInfo)
51+
private static IServiceCollection GetServiceCollection(MethodInfo methodInfo)
5152
{
52-
return (IServiceCollection)methodInfo.Invoke(null, null);
53+
var serviceCollection = methodInfo.Invoke(null, null);
54+
if(methodInfo.ReturnType == typeof(void))
55+
{
56+
throw new InvalidScenarioDependenciesException("the method doesn't return a value.");
57+
}
58+
59+
if (serviceCollection == null)
60+
{
61+
throw new InvalidScenarioDependenciesException("returned null.");
62+
}
63+
64+
if (serviceCollection is not IServiceCollection collection)
65+
{
66+
throw new InvalidScenarioDependenciesException($"returned {serviceCollection.GetType()}.");
67+
}
68+
return collection;
5369
}
5470

5571
private static void AddBindingAttributes(IEnumerable<Assembly> bindingAssemblies, IServiceCollection serviceCollection)

Tests/Reqnroll.PluginTests/Microsoft.Extensions.DependencyInjection/ServiceCollectionFinderTests.cs

+95-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Linq;
34
using System.Reflection;
45
using FluentAssertions;
@@ -27,7 +28,68 @@ public void GetServiceCollection_HappyPath_ResolvesCorrectServiceCollection()
2728
var testService = serviceProvider.GetRequiredService<ITestInterface>();
2829
testService.Name.Should().Be("ValidStartWithAutoRegister");
2930
}
30-
31+
32+
[Fact]
33+
public void GetServiceCollection_MethodIsVoid_ThrowsInvalidScenarioDependenciesException()
34+
{
35+
// Arrange
36+
var testRunnerManagerMock = CreateTestRunnerManagerMock(typeof(InvalidStartVoid));
37+
var sut = new ServiceCollectionFinder(testRunnerManagerMock.Object);
38+
39+
// Act
40+
var act = () => sut.GetServiceCollection();
41+
42+
// Assert
43+
act.Should().Throw<InvalidScenarioDependenciesException>()
44+
.WithMessage("[ScenarioDependencies] should return IServiceCollection but the method doesn't return a value.");
45+
}
46+
47+
[Fact]
48+
public void GetServiceCollection_MethodReturnsNull_ThrowsInvalidScenarioDependenciesException()
49+
{
50+
// Arrange
51+
var testRunnerManagerMock = CreateTestRunnerManagerMock(typeof(InvalidStartNull));
52+
var sut = new ServiceCollectionFinder(testRunnerManagerMock.Object);
53+
54+
// Act
55+
var act = () => sut.GetServiceCollection();
56+
57+
// Assert
58+
act.Should().Throw<InvalidScenarioDependenciesException>()
59+
.WithMessage("[ScenarioDependencies] should return IServiceCollection but returned null.");
60+
}
61+
62+
63+
[Fact]
64+
public void GetServiceCollection_MethodReturnsInvalidType_ThrowsInvalidScenarioDependenciesException()
65+
{
66+
// Arrange
67+
var testRunnerManagerMock = CreateTestRunnerManagerMock(typeof(InvalidStartWrongType));
68+
var sut = new ServiceCollectionFinder(testRunnerManagerMock.Object);
69+
70+
// Act
71+
var act = () => sut.GetServiceCollection();
72+
73+
// Assert
74+
act.Should().Throw<InvalidScenarioDependenciesException>()
75+
.WithMessage("[ScenarioDependencies] should return IServiceCollection but returned System.Collections.Generic.List`1[System.String].");
76+
}
77+
78+
[Fact]
79+
public void GetServiceCollection_NotFound_ThrowsMissingScenarioDependenciesException()
80+
{
81+
// Arrange
82+
var testRunnerManagerMock = CreateTestRunnerManagerMock(typeof(ServiceCollectionFinderTests));
83+
var sut = new ServiceCollectionFinder(testRunnerManagerMock.Object);
84+
85+
// Act
86+
var act = () => sut.GetServiceCollection();
87+
88+
// Assert
89+
act.Should().Throw<MissingScenarioDependenciesException>()
90+
.WithMessage("No method marked with [ScenarioDependencies] attribute found. It should be a (public or non-public) static method. Scanned assemblies: Reqnroll.PluginTests.");
91+
}
92+
3193
[Fact]
3294
public void GetServiceCollection_AutoRegisterBindingsTrue_RegisterBindingsAsScoped()
3395
{
@@ -62,6 +124,11 @@ private static Mock<ITestRunnerManager> CreateTestRunnerManagerMock(params Type[
62124
{
63125
var assemblyMock = new Mock<Assembly>();
64126
assemblyMock.Setup(m => m.GetTypes()).Returns(types.ToArray());
127+
if (types.Length > 0)
128+
{
129+
var assembly = types[0].Assembly;
130+
assemblyMock.Setup(m => m.GetName()).Returns(assembly.GetName());
131+
}
65132

66133
var testRunnerManagerMock = new Mock<ITestRunnerManager>();
67134
testRunnerManagerMock.Setup(m => m.BindingAssemblies).Returns([assemblyMock.Object]);
@@ -94,6 +161,33 @@ public static IServiceCollection GetServices()
94161
return serviceCollection;
95162
}
96163
}
164+
private class InvalidStartVoid
165+
{
166+
[ScenarioDependencies]
167+
public static void GetServices()
168+
{
169+
var serviceCollection = new ServiceCollection();
170+
serviceCollection.AddSingleton<ITestInterface>(new TestInterface("ValidStartWithAutoRegister"));
171+
}
172+
}
173+
174+
private class InvalidStartNull
175+
{
176+
[ScenarioDependencies]
177+
public static IServiceCollection GetServices()
178+
{
179+
return null;
180+
}
181+
}
182+
183+
private class InvalidStartWrongType
184+
{
185+
[ScenarioDependencies]
186+
public static object GetServices()
187+
{
188+
return new List<string>();
189+
}
190+
}
97191
[Binding]
98192
private class Binding1;
99193
}

0 commit comments

Comments
 (0)