jkokorian
jkokorian

Reputation: 3095

How to register a custom ITraceListener in SpecFlow 3.9.74

We are upgrading from specflow 1.something to version 3.9.74. I'm having some trouble getting our custom TraceListener to work.

This is the code of the IRuntimePlugin:

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

[assembly: RuntimePlugin(typeof(SpecFlowTracerPlugin.SpecFlowTracerPlugin))]

namespace SpecFlowTracerPlugin
{
    public class SpecFlowTracerPlugin : IRuntimePlugin
    {
        public void Initialize(RuntimePluginEvents runtimePluginEvents, RuntimePluginParameters runtimePluginParameters, UnitTestProviderConfiguration unitTestProviderConfiguration)
        {
            runtimePluginEvents.CustomizeTestThreadDependencies +=
                (sender, args) => { args.ObjectContainer.RegisterTypeAs<SpecFlowTracer, ITraceListener>(); };
        }
    }
}

This is the code of the TraceListener class (it uses our own tracing framework, filters out some noise, and ensures that the test output is written to the output while test is running, not only at the end):

    
public class SpecFlowTracer : ITraceListener
    {
        private readonly Trace _trace = new Trace("SpecFlow", "TestStep");

        public void WriteTestOutput(string message) => Trace(message);

        public void WriteToolOutput(string message) => Trace(message);

        private void Trace(string message)
        {
            if (message.StartsWith("done: ")) return;
            var context = FeatureContext.Current;
            if (context != null)
            {
                message = $"{DateTime.Now:yy-MM-dd HH:mm:ss.fff}\t{context.FeatureInfo.Title}: {message}";
            }
            _trace.TraceInfo($"-> {message}");
            // Next line is required to show progress while test is running
            TestContext.Progress.WriteLine(message);
            // Next line is required to show output in xml and other places
            Console.WriteLine(message);
        }
    }

Both classes are defined in a class library that is used as a plugin by the main test project. In our specflow test project, the class library is is included as a reference. In the app.config file of the test project there used to be this code:

<specflow>
    ...
    <plugins>
      <add name="SpecFlowTracer" path="." type="Runtime" />
    </plugins>
</specFlow>

However, this is no longer needed in specflow 3 according to the documentation (it doesn't even compile). Instead, plugins are detected automatically when their dll is present in the bin directory of the test project. And indeed, when running a test with the debugger attached, I can see that the Initialize method is called correctly, but the constructor of the SpecFlowTracer class is never called (I checked by adding an empty default constructor with a breakpoint in it). It appears that the default implementation is still used.

I've also tried registering it using the following code in the IRuntimePlugin's Initialize method:

public void Initialize(RuntimePluginEvents runtimePluginEvents, RuntimePluginParameters runtimePluginParameters, UnitTestProviderConfiguration unitTestProviderConfiguration)
{
    runtimePluginEvents.RegisterGlobalDependencies += (sender, args) =>
    {
        args.ObjectContainer.RegisterTypeAs<SpecFlowTracer, ITraceListener>();
    };
}

But that results in an exception being thrown:

BoDi.ObjectContainerException: 'An object has been resolved for this interface already.'

Update: I've tried the same with CustomizeGlobalDependencies instead of RegisterGlobalDependencies, but the same exception is throw.

When I inspect the registrations property of the objectContainer, I can see that there is indeed an existing registration for ITraceListener: the TechTalk.SpecFlow.Tracing.DefaultTraceListener.

What is the correct way to register a tracelistener?

Upvotes: 2

Views: 90

Answers (0)

Related Questions