SpecFlowOSS / SpecFlow

#1 .NET BDD Framework. SpecFlow automates your testing & works with your existing code. Find Bugs before they happen. Behavior Driven Development helps developers, testers, and business representatives to get a better understanding of their collaboration
https://www.specflow.org/
Other
2.23k stars 752 forks source link

Documentation request: enhance plugin documentation for changing global dependencies #2594

Open ghost opened 2 years ago

ghost commented 2 years ago

Product:

What is missing: I tried to change the default ITraceListener like so:

public class CustomTracerPlugin : IRuntimePlugin
{
    public void Initialize(
        RuntimePluginEvents runtimePluginEvents,
        RuntimePluginParameters runtimePluginParameters,
        UnitTestProviderConfiguration unitTestProviderConfiguration)
    {
        runtimePluginEvents.CustomizeGlobalDependencies += (_, args) => args.ObjectContainer.RegisterTypeAs<CustomTracer, ITraceListener>();
    }
}

The initialize function gets called and failes, because there is already a ITraceListener registered. Documentation should include an example on how to change default objects in the DI-container.

SabotageAndi commented 2 years ago

You picked the one interface that can't really be overridden by plugins on another level than on Scenarios. We are using it for logging itself before the plugins are loaded.

What did you try to achieve?

ghost commented 2 years ago

I see you set this in the NUnit plugin.

I would really like to reduce output to a bare minimum for running tests on gitlab for merge request tests.

Ideally we would like to only get output in the form: test X successful and the full output for failing tests.

ghost commented 2 years ago

It would be all right to overwrite it on scenario level I don't need the output of all the successful steps. If I can overwrite it on scenario level and remove most of the output that would be perfect. We have a limit on the log size (trying to remove that, too).

SabotageAndi commented 2 years ago

Did you try to turn the tracing off?

https://docs.specflow.org/projects/specflow/en/latest/Installation/Configuration.html#trace

If this is not enough, I think your requirement would be best solved, if you contribute it to SpecFlow. Either use the tracing configuration value in more places or add a new one for the log level.

ghost commented 2 years ago

I did, here's my specflow.json:

{
    "language": {
        "feature": "de-DE"
    },
    "stepAssemblies": [
        {
            "assembly": "SomeAssembly"
        }
    ],
    "trace": {
        "traceSuccessfulSteps": false
    }
}

And I will think about your proposal.

ghost commented 2 years ago

should I file a bug report because traceSuccessfulSteps has no effect?

SabotageAndi commented 2 years ago

Before that, can you check your specflow.json file is in the output folder? To be sure where the error is.

dennisschagt commented 3 months ago

I came across this issue a few times while looking for a way to inject an ITraceListener to capture start/end of step logging. (we were previously using the <trace listener="..." /> config option in Specflow 2 but can't use that anymore with Specflow 3) Turns out that start/end of step logging is done using the ITraceListener instance of the "test thread" container. Reproducing our solution here just in case it helps the next person (although I'm not sure if this is a stable solution. It looks like the nunit/xunit/mstest plugins also override ITraceListener. I don't know if the order of loading their plugin and our own CustomTracerPlugin is well-defined):

using TechTalk.SpecFlow.Plugins;
using TechTalk.SpecFlow.Tracing;
using TechTalk.SpecFlow.UnitTestProvider;

[assembly: RuntimePlugin(typeof(CustomTracerPlugin))]

public class CustomTracerPlugin : IRuntimePlugin
{
    public void Initialize(
        RuntimePluginEvents runtimePluginEvents,
        RuntimePluginParameters runtimePluginParameters,
        UnitTestProviderConfiguration unitTestProviderConfiguration)
    {
        runtimePluginEvents.CustomizeTestThreadDependencies +=
            (s, ea) => ea.ObjectContainer.RegisterTypeAs<OurNameSpace.OurClassName, ITraceListener>();
    }
}

namespace OurNameSpace
{
    public class OurClassName : ITraceListener {
        // Implementation
    }
}

You picked the one interface that can't really be overridden by plugins on another level than on Scenarios. We are using it for logging itself before the plugins are loaded.

Before finding the above solution, I tried to override the ITraceListener instance in the global dependency injection container. Doing it via a runtime plugin did not work indeed. However, I did find a way using the specflow.json config. It looks like the JsonConfigurationLoader registers any (implementation, interface) pairs that are specified in runtime.dependencies. In our case, we got that to work with the following specflow.json:

{
  "$schema": "https://specflow.org/specflow-config.json",
  "runtime": {
    "missingOrPendingStepsOutcome": "Error",
    "dependencies": [
      {
        "type": "OurNameSpace.OurClassName, OurAssemblyName",
        "as": "TechTalk.SpecFlow.Tracing.ITraceListener, TechTalk.SpecFlow"
      }
    ]
  }
}