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

Improve error message provided by Reqnroll.Microsoft.Extensions.DependencyInjection plugin when a test execution dependent service is required during [BeforeTestRun] #318

Merged
merged 4 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 4 additions & 11 deletions .ncrunch/Reqnroll.PluginTests.v3.ncrunchproject
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,9 @@
<AdditionalFilesToIncludeForProject>
<Value>ExternalData\SampleFiles\**.*</Value>
</AdditionalFilesToIncludeForProject>
<IgnoredTests>
<NamedTestSelector>
<TestName>Reqnroll.PluginTests.Generator.GeneratorPluginLoaderTests.LoadPlugin_LoadXUnitSuccessfully</TestName>
</NamedTestSelector>
<NamedTestSelector>
<TestName>Reqnroll.PluginTests.Infrastructure.WindsorPluginTests.Can_load_Windsor_plugin</TestName>
</NamedTestSelector>
<NamedTestSelector>
<TestName>Reqnroll.PluginTests.Infrastructure.MicrosoftExtensionsDependencyInjectionTests.LoadPlugin_MicrosoftExtensionsDependencyInjection_ShouldNotBeNull</TestName>
</NamedTestSelector>
</IgnoredTests>
<CopyReferencedAssembliesToWorkspace>True</CopyReferencedAssembliesToWorkspace>
<HiddenComponentWarnings>
<Value>CopyReferencedAssembliesToWorkspaceIsOn</Value>
</HiddenComponentWarnings>
</Settings>
</ProjectConfiguration>
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
* Fix: Visual Studio locks Reqnroll.Tools.MsBuild.Generation task files. Using `TaskHostFactory` for our tasks on Windows. (#293)
* Fix: Project dependencies transiently refer to System.Text.Json 8.0.4 that has security vulnerability. Microsoft.Extensions.DependencyModel updated to v8.0.2. (#291)
* Fix: Could not load System.CodeDom exception with xRetry.Reqnroll plugin (#310)
* Fix: Reqnroll.Microsoft.Extensions.DependencyInjection: `System.Collections.Generic.KeyNotFoundException: The given key 'Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope' was not present in the dictionary.` is thrown when a test execution dependent service is required during [BeforeTestRun]. We provide a better error message now. (#175)

*Contributors of this release (in alphabetical order):* @gasparnagy, @obligaron, @Romfos, @Tiberriver256

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,26 +157,41 @@ private static void RegisterProxyBindings(IObjectContainer objectContainer, ISer
services.AddSingleton(sp => objectContainer.Resolve<IBindingAssemblyLoader>());
services.AddSingleton(sp => objectContainer.Resolve<IUnitTestRuntimeProvider>());

services.AddTransient(sp =>
TResult GetTestThreadDependency<TResult>(IServiceProvider sp, Func<IContextManager, TResult> selector) where TResult: class
{
var container = BindMappings.TryGetValue(sp, out var ctx)
? ctx.ScenarioContext?.ScenarioContainer ??
ctx.FeatureContext?.FeatureContainer ??
ctx.TestThreadContext?.TestThreadContainer ??
objectContainer
: objectContainer;

return container.Resolve<IReqnrollOutputHelper>();
});

services.AddTransient(sp => BindMappings[sp]);
services.AddTransient(sp => BindMappings[sp].TestThreadContext);
services.AddTransient(sp => BindMappings[sp].FeatureContext);
services.AddTransient(sp => BindMappings[sp].ScenarioContext);
services.AddTransient(sp => BindMappings[sp].TestThreadContext.TestThreadContainer.Resolve<ITestRunner>());
services.AddTransient(sp => BindMappings[sp].TestThreadContext.TestThreadContainer.Resolve<ITestExecutionEngine>());
services.AddTransient(sp => BindMappings[sp].TestThreadContext.TestThreadContainer.Resolve<IStepArgumentTypeConverter>());
services.AddTransient(sp => BindMappings[sp].TestThreadContext.TestThreadContainer.Resolve<IStepDefinitionMatchService>());
string GetErrorMessage()
=> $"Unable to access test execution dependent service '{typeof(TResult).FullName}' with the Reqnroll.Microsoft.Extensions.DependencyInjection plugin. This service is only available once test execution has been started and cannot be used in '[BeforeTestRun]' hook. See https://go.reqnroll.net/doc-migrate-specflow-testrun-hooks for details.";

if (!BindMappings.TryGetValue(sp, out var contextManager))
{
throw new ReqnrollException(GetErrorMessage());
}

TResult result;
try
{
result = selector(contextManager);
}
catch (Exception ex)
{
throw new ReqnrollException(GetErrorMessage(), ex);
}

if (result == null)
throw new ReqnrollException(GetErrorMessage());

return result;
}

services.AddTransient(sp => GetTestThreadDependency(sp, cm => cm));
services.AddTransient(sp => GetTestThreadDependency(sp, cm => cm.TestThreadContext));
services.AddTransient(sp => GetTestThreadDependency(sp, cm => cm.FeatureContext));
services.AddTransient(sp => GetTestThreadDependency(sp, cm => cm.ScenarioContext));
services.AddTransient(sp => GetTestThreadDependency(sp, cm => cm.TestThreadContext.TestThreadContainer.Resolve<ITestRunner>()));
services.AddTransient(sp => GetTestThreadDependency(sp, cm => cm.TestThreadContext.TestThreadContainer.Resolve<ITestExecutionEngine>()));
services.AddTransient(sp => GetTestThreadDependency(sp, cm => cm.TestThreadContext.TestThreadContainer.Resolve<IStepArgumentTypeConverter>()));
services.AddTransient(sp => GetTestThreadDependency(sp, cm => cm.TestThreadContext.TestThreadContainer.Resolve<IStepDefinitionMatchService>()));
services.AddTransient(sp => GetTestThreadDependency(sp, cm => cm.TestThreadContext.TestThreadContainer.Resolve<IReqnrollOutputHelper>()));
}

private class RootServiceProviderContainer
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using FluentAssertions;
using Moq;
using Reqnroll.Plugins;
using Reqnroll.Tracing;
using Xunit;

namespace Reqnroll.PluginTests.Microsoft.Extensions.DependencyInjection;

public class MicrosoftExtensionsDependencyInjectionTests
{
[Fact]
public void LoadPlugin_MicrosoftExtensionsDependencyInjection_ShouldNotBeNull()
{
var loader = new RuntimePluginLoader(new PluginAssemblyLoader());
var listener = new Mock<ITraceListener>();

var plugin = loader.LoadPlugin("Reqnroll.Microsoft.Extensions.DependencyInjection.ReqnrollPlugin.dll", listener.Object, It.IsAny<bool>());

plugin.Should().NotBeNull();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@
using System.Collections.Specialized;
using System.Globalization;
using System.Reflection;
using Reqnroll.BoDi;
using Castle.Windsor;
using FluentAssertions;
using Moq;
using Reqnroll.Windsor;
using Reqnroll.Bindings;
using Reqnroll.BoDi;
using Reqnroll.Configuration;
using Reqnroll.Infrastructure;
using Reqnroll.Plugins;
using Reqnroll.Tracing;
using Reqnroll.Windsor;
using Xunit;

namespace Reqnroll.PluginTests.Infrastructure
namespace Reqnroll.PluginTests.Windsor
{
public class WindsorPluginTests
{
Expand Down