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.25k stars 754 forks source link

Generator plugin load fails with FileNotFoundException: TechTalk.Specflow cannot be loaded #1476

Closed Cylox closed 5 years ago

Cylox commented 5 years ago

SpecFlow Version:

Used Test Runner

Version number: 3.10

Visual Studio Version

Are the latest Visual Studio updates installed?

.NET Framework:

Test Execution Method: N/A

<SpecFlow> Section in app.config or content of specflow.json

  <specFlow>
    <plugins>
      <add name="Company.MyPlugin" type="GeneratorAndRuntime" path="..\packages\Company.MyPlugin.SpecFlowPlugin.4.0.0-preview006\lib\net471"></add>
    </plugins>
    <unitTestProvider name="NUnit" />    
  <stepAssemblies>
      <stepAssembly assembly="Company.StepDefinitions" />
    </stepAssemblies>
  </specFlow>

Repro Project

https://github.com/Cylox/SpecflowPluginLoadFail

Issue Description

After upgrading to SpecFlow 3 from 2.2.1 I cannot load my generator plugin anymore. When I try to load the plugin from the nuget package directory (as I did before the upgrade), after the code generation process I get the message

We could not find a data exchange file at the path System.IO.FileNotFoundException: Could not load file or assembly 'TechTalk.SpecFlow, Version=3.0.0.0, Culture=neutral, PublicKeyToken=0778194805d6db41' or one of its dependencies. The system cannot find the file specified.

Please open an issue at https://github.com/techtalk/SpecFlow/issues/
Complete output: 
System.IO.FileNotFoundException: Could not load file or assembly 'TechTalk.SpecFlow, Version=3.0.0.0, Culture=neutral, PublicKeyToken=0778194805d6db41' or one of its dependencies. The system cannot find the file specified.
File name: 'TechTalk.SpecFlow, Version=3.0.0.0, Culture=neutral, PublicKeyToken=0778194805d6db41'
   at System.ModuleHandle.ResolveType(RuntimeModule module, Int32 typeToken, IntPtr* typeInstArgs, Int32 typeInstCount, IntPtr* methodInstArgs, Int32 methodInstCount, ObjectHandleOnStack type)
   at System.ModuleHandle.ResolveTypeHandleInternal(RuntimeModule module, Int32 typeToken, RuntimeTypeHandle[] typeInstantiationContext, RuntimeTypeHandle[] methodInstantiationContext)
   at System.Reflection.RuntimeModule.ResolveType(Int32 metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments)
   at System.Reflection.CustomAttribute.FilterCustomAttributeRecord(CustomAttributeRecord caRecord, MetadataImport scope, Assembly& lastAptcaOkAssembly, RuntimeModule decoratedModule, MetadataToken decoratedToken, RuntimeType attributeFilterType, Boolean mustBeInheritable, Object[] attributes, IList derivedAttributes, RuntimeType& attributeType, IRuntimeMethodInfo& ctor, Boolean& ctorHasParameters, Boolean& isVarArg)
   at System.Reflection.CustomAttribute.GetCustomAttributes(RuntimeModule decoratedModule, Int32 decoratedMetadataToken, Int32 pcaCount, RuntimeType attributeFilterType, Boolean mustBeInheritable, IList derivedAttributes, Boolean isDecoratedTargetSecurityTransparent)
   at System.Reflection.CustomAttribute.GetCustomAttributes(RuntimeAssembly assembly, RuntimeType caType)
   at System.Attribute.GetCustomAttributes(Assembly element, Type attributeType, Boolean inherit)
   at System.Attribute.GetCustomAttribute(Assembly element, Type attributeType, Boolean inherit)
   at TechTalk.SpecFlow.Generator.Plugins.GeneratorPluginLoader.LoadPlugin(PluginDescriptor pluginDescriptor)
   at TechTalk.SpecFlow.Generator.GeneratorContainerBuilder.<>c__DisplayClass3.<LoadPlugins>b__1(PluginDescriptor pd)
   at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
   at System.Linq.Enumerable.<ConcatIterator>d__59`1.MoveNext()
   at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
   at TechTalk.SpecFlow.Generator.GeneratorContainerBuilder.CreateContainer(SpecFlowConfigurationHolder configurationHolder, ProjectSettings projectSettings)
   at TechTalk.SpecFlow.Generator.TestGeneratorFactory.CreateGenerator(ProjectSettings projectSettings)
   at TechTalk.SpecFlow.VisualStudio.CodeBehindGenerator.Actions.GenerateTestFileAction.GenerateTestFile(GenerateTestFileParameters opts)

=== Pre-bind state information ===
LOG: DisplayName = TechTalk.SpecFlow, Version=3.0.0.0, Culture=neutral, PublicKeyToken=0778194805d6db41
 (Fully-specified)
LOG: Appbase = file:///C:/WINDOWS/system32
LOG: Initial PrivatePath = NULL
Calling assembly : MyPlugin.SpecFlowPlugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null.
===
LOG: This bind starts in LoadFrom load context.
WRN: Native image will not be probed in LoadFrom context. Native image will only be probed in default load context, like with Assembly.Load().
LOG: Using application configuration file: c:\users\chrpabst\appdata\local\microsoft\visualstudio\15.0_0bca4cab\extensions\uob2eazz.cq5\TechTalk.SpecFlow.VisualStudio.CodeBehindGenerator.exe.Config
LOG: Using host configuration file: 
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework64\v4.0.30319\config\machine.config.
LOG: Post-policy reference: TechTalk.SpecFlow, Version=3.0.0.0, Culture=neutral, PublicKeyToken=0778194805d6db41
LOG: Attempting download of new URL file:///C:/WINDOWS/system32/TechTalk.SpecFlow.DLL.
LOG: Attempting download of new URL file:///C:/WINDOWS/system32/TechTalk.SpecFlow/TechTalk.SpecFlow.DLL.
LOG: Attempting download of new URL file:///C:/WINDOWS/system32/TechTalk.SpecFlow.EXE.
LOG: Attempting download of new URL file:///C:/WINDOWS/system32/TechTalk.SpecFlow/TechTalk.SpecFlow.EXE.
LOG: Attempting download of new URL file:///C:/Users/chrpabst/source/repos/PluginLoadTest/MyPlugin.SpecFlowPlugin/bin/Debug/TechTalk.SpecFlow.DLL.
LOG: Attempting download of new URL file:///C:/Users/chrpabst/source/repos/PluginLoadTest/MyPlugin.SpecFlowPlugin/bin/Debug/TechTalk.SpecFlow/TechTalk.SpecFlow.DLL.
LOG: Attempting download of new URL file:///C:/Users/chrpabst/source/repos/PluginLoadTest/MyPlugin.SpecFlowPlugin/bin/Debug/TechTalk.SpecFlow.EXE.
LOG: Attempting download of new URL file:///C:/Users/chrpabst/source/repos/PluginLoadTest/MyPlugin.SpecFlowPlugin/bin/Debug/TechTalk.SpecFlow/TechTalk.SpecFlow.EXE.

Command: c:\users\chrpabst\appdata\local\microsoft\visualstudio\15.0_0bca4cab\extensions\uob2eazz.cq5\TechTalk.SpecFlow.VisualStudio.CodeBehindGenerator.exe
Parameters: GenerateTestFile --featurefile C:\Users\chrpabst\AppData\Local\Temp\tmpA32F.tmp --outputdirectory C:\Users\chrpabst\AppData\Local\Temp --projectsettingsfile C:\Users\chrpabst\AppData\Local\Temp\tmpA32E.tmp
Working Directory: 

When I add the specflow assembly in the same version as the test assembly uses to the plugin directory I get:

We could not find a data exchange file at the path TechTalk.SpecFlow.SpecFlowException: Missing [assembly:GeneratorPlugin] attribute in C:\Users\chrpabst\source\repos\PluginLoadTest\MyPlugin.SpecFlowPlugin\bin\Debug\MyPlugin.SpecFlowPlugin.dll

Please open an issue at https://github.com/techtalk/SpecFlow/issues/
Complete output: 
TechTalk.SpecFlow.SpecFlowException: Missing [assembly:GeneratorPlugin] attribute in C:\Users\chrpabst\source\repos\PluginLoadTest\MyPlugin.SpecFlowPlugin\bin\Debug\MyPlugin.SpecFlowPlugin.dll
   at TechTalk.SpecFlow.Generator.Plugins.GeneratorPluginLoader.LoadPlugin(PluginDescriptor pluginDescriptor)
   at TechTalk.SpecFlow.Generator.GeneratorContainerBuilder.<>c__DisplayClass3.<LoadPlugins>b__1(PluginDescriptor pd)
   at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
   at System.Linq.Enumerable.<ConcatIterator>d__59`1.MoveNext()
   at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
   at TechTalk.SpecFlow.Generator.GeneratorContainerBuilder.CreateContainer(SpecFlowConfigurationHolder configurationHolder, ProjectSettings projectSettings)
   at TechTalk.SpecFlow.Generator.TestGeneratorFactory.CreateGenerator(ProjectSettings projectSettings)
   at TechTalk.SpecFlow.VisualStudio.CodeBehindGenerator.Actions.GenerateTestFileAction.GenerateTestFile(GenerateTestFileParameters opts)

Command: c:\users\chrpabst\appdata\local\microsoft\visualstudio\15.0_0bca4cab\extensions\uob2eazz.cq5\TechTalk.SpecFlow.VisualStudio.CodeBehindGenerator.exe
Parameters: GenerateTestFile --featurefile C:\Users\chrpabst\AppData\Local\Temp\tmp68E2.tmp --outputdirectory C:\Users\chrpabst\AppData\Local\Temp --projectsettingsfile C:\Users\chrpabst\AppData\Local\Temp\tmp68E1.tmp
Working Directory: 

Steps to Reproduce

Clone the example repo and try to generate the code behind files via "Run custom tool". I have used the latest version of the VS extension. Add/remove the specflow assemblies from the plugin directory to switch between the error messages.

SabotageAndi commented 5 years ago

We changed how plugins work with SpecFlow 3. There is now a little bit of MSBuild magic involved.

I think we are not yet completely done with the documentation. But NUnit, XUnit and MSTest have now also plugins. Have a look at the source code of them.

I will check how far we are with the documentation for it. Could take some hours, as I am on my way to London.

Cylox commented 5 years ago

@SabotageAndi thanks for your fast response! Okay, so I assume this means that the old way of specifying the plugin just in the app.config is not sufficent anymore or obsolete. It would be great if the plugin documentation page could reflect this. I've seen the MSBuild generator package but since I found no documentation how to integrate a custom generator with this package I tried to do it the old way. If plugin definition in the app.config file is not supported anymore it would be great to write a meaningful error message to the feature.cs file in case a plugin section is detected.

SabotageAndi commented 5 years ago

Good point with the plugin config section. No idea why it's still there. I have to check.

The biggest change for the plugins, that for generator plugins, a MSBuild ItemGroup has to be filled. Here is it for xUnit: https://github.com/techtalk/SpecFlow/blob/master/Plugins/TechTalk.SpecFlow.xUnit.Generator.SpecFlowPlugin/build/SpecFlow.xUnit.props We build our generator plugins currently for .NET 4.7.1 and .NET Standard. This is needed because there is a .NET Full Framework version of MSBuild and a .NET Core version. So a little if is needed, to put the correct assembly into the itemgroup. This is made here https://github.com/techtalk/SpecFlow/blob/master/Plugins/TechTalk.SpecFlow.xUnit.Generator.SpecFlowPlugin/build/SpecFlow.xUnit.targets

zplan commented 5 years ago

I have a similar issue:

I have my own generator in Secflow 2.x I used it like this:

App.config:

<configuration>
<configSections>
<section name="specFlow" type="TechTalk.SpecFlow.Configuration.ConfigurationSectionHandler,TechTalk.SpecFlow" />
  </configSections>
 <specFlow>
    <unitTestProvider name="mstest.v2" />
    <trace traceSuccessfulSteps="false" />
    <plugins>
      <add name="MyGenerator" path="..\MyGenerator.SpecflowPlugin" type="Generator">
</add>
    </plugins>    
  </specFlow>  
</configuration>

I updated the generator now to Specflow 3, By trying the same app.config i was not successful

I get an error message: Error [SpecFlow] System.Configuration.ConfigurationErrorsException: Unrecognized element 'unitTestProvider'.

Then I changed it to specflow.json like this:

{
  "language": {
    "feature": "en-US"
  },
  "trace": {
    "traceSuccessfulSteps": false
  },
  "plugins": {
    "add": {
      "name": "MyGenerator",
      "path": "..\\MyGenerator.SpecflowPlugin",
      "type": "Generator"
    }
  }
}

But also no success. It just uses the default Specflow generator.

@SabotageAndi : Can you give me an example how it has to be configured now with SpecFlow3 and MSTest custom generator?

Thanks

Cylox commented 5 years ago

@SabotageAndi I was able to fix my plugin due to the guidance you provided with the plugins contained within the SpecFlow repo. @zplan It basically boils down to copying and adapting the .props and .targets files from the build directory. Instead of specifying the path in the app.config, specify the path in the .targets file.

One really great improvement by the way is the generation speed of the feature.cs files. It has improved dramatically over the generation process within the VS extension. Many thanks for this!

SabotageAndi commented 5 years ago

@Cylox good to hear that you was successful.

nuzolx commented 5 years ago

@Cylox how did you load plugins? specflow.json or something in the csproj?

Cylox commented 5 years ago

@nuzolx I added the necessary build files to the plugin package, just like it is done with the plugins that are contained within the specflow repository. Then I removed all plugin sections from the app.config files in the tests that use the plugin. I have only verified the generator functionality of the plugin so far, but I have also a runtime plugin whose functionality I have not verified yet. For a blueprint how to add the msbuild files to your plugin package have a look at the xUnit or NUnit plugin: https://github.com/techtalk/SpecFlow/tree/master/Plugins/TechTalk.SpecFlow.NUnit.Generator.SpecFlowPlugin https://github.com/techtalk/SpecFlow/tree/master/Plugins/TechTalk.SpecFlow.xUnit.Generator.SpecFlowPlugin

nuzolx commented 5 years ago

@Cylox Sorry but this to me seems a little too magical. You have to include the plugin at some point for it to be used, right?

Edit: I understood that it was necessary to include the props / targets in my tests projects.

@Cylox Thx for you help. @SabotageAndi Thx about you job.

nuzolx commented 5 years ago

Andy idea how to debug custom plugin? Before it was possible to attach to VS AppDomain but right now...no idea.

SabotageAndi commented 5 years ago

Debug.Launch/Debug.Break?

SabotageAndi commented 5 years ago

@Cylox problem was solved so I will close this issue.

The plugin docs are now updated and are located at https://github.com/techtalk/SpecFlow/blob/master/docs/plugins/Writing%20Plugins%20for%20SpecFlow%203.md or https://specflow.org/documentation/Plugins/

We also created a Gitter channel special for plugin development: https://gitter.im/techtalk/specflow-plugin-dev We did this, that the developer of plugins can easily talk to each other and help out if needed. We will also use this channel to announce changes to the plugin interfaces, so that you aren't surprised when a new version is released.

lock[bot] commented 5 years ago

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.