I have seen the question here How to execute before and after test run only once in parallel execution of specflow - although I am unable to understand how to run the Hooks just once in parallel
I am using Specflow+ Runner as my test provider, my thread count is 3, I have a BeforeTestRun hook that creates my test data, I want to execute that Hook only once, but by default BeforeTestRun is ran on each thread, I have also looked at the example here:
https://github.com/SpecFlowOSS/SpecFlow.Plus.Examples/tree/master/CustomDeploymentSteps,
But I am unsure how it works, could anyone help me out on this one.
[BeforeTestRun()]
public static void BeforeTestRun()
{
Console.WriteLine("test");
}
Output is:
test
test
test
My Default.srprofile looks like this
<?xml version="1.0" encoding="utf-8"?>
<TestProfile xmlns="http://www.specflow.org/schemas/plus/TestProfile/1.5">
<Settings projectName="TestAutoProject" />
<Execution stopAfterFailures="2" testThreadCount="3" testSchedulingMode="Sequential" />
<TestAssemblyPaths>
<TestAssemblyPath>Test.dll</TestAssemblyPath>
</TestAssemblyPaths>
<Targets>
<Target name="IE">
<Filter>Browser_IE</Filter>
<DeploymentTransformationSteps>
<EnvironmentVariable variable="Test_Browser" value="IE" />
</DeploymentTransformationSteps>
</Target>
<Target name="Chrome">
<Filter>Browser_Chrome</Filter>
<DeploymentTransformationSteps>
<EnvironmentVariable variable="Test_Browser" value="Chrome" />
</DeploymentTransformationSteps>
</Target>
<Target name="Firefox">
<Filter>Browser_Firefox</Filter>
<DeploymentTransformationSteps>
<EnvironmentVariable variable="Test_Browser" value="Firefox" />
</DeploymentTransformationSteps>
</Target>
</Targets>
<DeploymentTransformation>
<Steps></Steps>
</DeploymentTransformation>
</TestProfile>
Related
We have a config file that is edited by our GUI to dynamically change the log level of our file target.
Our filename looks like this: fileName="${logDir}${var:subLogDir}/${var:regionName}${logName}.dlog"
All of the variables are in the base .config file of the program and the regionName is set dynamically when the program starts by setting
variable directly in the configuration via NLog.LogManager.Configuration.Variables["regionName"] = string.Concat(RegionName, "/");.
Our problem is when we modify the log level while the program is generating logs, there will be a moment where the logs will fall into the ${var:subLogDir} directly as if the ${var:regionName} is not set even if we know it is.
It seems like there is a moment in time where the dynamic variables are not resolved yet. Is there something we can do to prevent this? Buffering the logs while reloading?
NLog version: 4.7.4
.config file
<nlog keepVariablesOnReload="true">
<variable name="subLogDir" value="/Regions" />
<include file="..\Common\Logs\BaseVariables.xml" />
<include file="..\Common\Logs\BaseTargets.xml" />
<include file="..\Common\Logs\BaseRules.xml" />
</nlog>
BaseVariables.xml
<nlog>
<!-- Default variables values -->
<variable name="logLevel" value="LogLevel.Info"/>
<variable name="callstackLogLevel" value="LogLevel.Debug"/>
<variable name="keepFileOpen" value="true"/>
<variable name="autoFlush" value="true"/>
<variable name="concurrentWrites" value="false"/>
<variable name="hasAudit" value="false"/>
<variable name="archiveAboveSize" value="52428800"/>
<variable name="maxArchiveDays" value="365"/>
<variable name="archiveNumbering" value="DateAndSequence"/>
</nlog>
BaseTargets.xml
<nlog>
<targets async="true">
<target Type="File" name="LogFile" createDirs="true" keepFileOpen="${keepFileOpen}" autoFlush="${autoFlush}"
fileName="${logDir}${var:subLogDir}/${var:regionName}${logName}.dlog"
concurrentWrites="${concurrentWrites}"
cleanupFileName="false"
archiveFileName="${archiveLogDir}${var:subLogDir}/${var:regionName}${archiveLogName}.dlog"
archiveEvery="${archiveEvery}" archiveAboveSize="${archiveAboveSize}"
archiveNumbering="${archiveNumbering}" archiveDateFormat="${archiveDateFormat}" maxArchiveDays="${maxArchiveDays}">
<layout Type="CSVLayout" delimiter="Tab" >
<column name="date" layout="${longdate}" />
<column name="level" layout="${uppercase:${level}}" />
<column name="source,keywords" layout="${logger}" />
<column name="message" layout="${message}" />
</layout>
</target>
</targets>
</nlog>
Thanks
You want the NLog Config Variables to behave in two ways:
When editing the NLog.config file, then it should discard the existing NLog Config Variables and take those when reloading the updated NLog.config.
This requires that you have configured KeepVariablesOnReload=false (default)
It would be easier if you had included NLog.config in your question. Instead of having to guess.
After the reload of the NLog.config then there is one NLog Config Variable (${var:regionName}) that you would like to keep.
Because you have configured KeepVariablesOnReload=false (default) then I guess you have hooked into LogManager.ConfigurationReloaded-event and re-assigning it manually.
It would be easier if you had included the logic for dynamic assigment of NLog config variable in your question. Instead of having to guess.
The LogManager.ConfigurationReloaded-event is called after the reloaded NLog Config has been assigned and initialized. So any logging happening until having dynamically re-assigned the ${var:regionName} will get empty value. See also https://github.com/NLog/NLog/pull/3954 and https://github.com/NLog/NLog/pull/3952
The current work-around is stop using LogManager.ConfigurationReloaded-event and instead just use NLog Global Diagnostic Context (GDC) for the single NLog Config Variable that needs to be restored on every reload. So it becomes ${gdc:regionName} and you assign it like this (once at startup):
NLog.GlobalDiagnosticsContext.Set("regionName", "Europe");
See also: https://github.com/NLog/NLog/wiki/Gdc-layout-renderer
Is there any way to get the Target name defined in specflow's srs profile ?
Below is what is defined in srs profile,
<Targets>
<Target name="Login">
<Filter>#login</Filter>
</Target>
</Targets>
I want to get the name of the target "Login" and save it in a variable in C#.
We don't have an API to get the target name, but you can get to it with a little bit more stuff in the srProfile.
This is how you do it:
<?xml version="1.0" encoding="utf-8"?>
<TestProfile xmlns="http://www.specflow.org/schemas/plus/TestProfile/1.5">
<Targets>
<Target name="Target1">
<DeploymentTransformationSteps>
<EnvironmentVariable variable="RUNNER_TARGET" value="Target1" />
</DeploymentTransformationSteps>
</Target>
<Target name="Target2">
<DeploymentTransformationSteps>
<EnvironmentVariable variable="RUNNER_TARGET" value="Target2" />
</DeploymentTransformationSteps>
</Target>
</Targets>
</TestProfile>
You specify a deployment transformation step which sets an environment variable to the name of the target.
In your bindings, you can get the value of the environment variable by normal .NET APIs.
var targetName = Environment.GetEnvironmentVariable("RUNNER_TARGET");
You can find a complete example here: https://github.com/SpecFlowOSS/SpecFlow.Plus.Examples/tree/master/AccessTargetName
Full disclosure: I am one of the developers of SpecFlow and SpecFlow+
I have a .NET Core 2.0 (Topshelf) console application that needs to send logs to Loggly. We have already implemented the same type of application using the NLog, NLog.Targets.Loggly, Topshelf.NLog, loggly-csharp-config, and loggly-csharp.
I added two additional targets, Console and text. Both of the other targets are working fine.
The customer Loggly key works fine in our .NET 4.71 apps of similar structure with the same packages.
Here is the package reference I have:
<ItemGroup>
<PackageReference Include="Autofac" Version="4.8.1" />
<PackageReference Include="loggly-csharp" Version="4.6.1.64" />
<PackageReference Include="loggly-csharp-config" Version="4.6.1.64" />
<PackageReference Include="NLog.Targets.Loggly" Version="4.7.0" />
<PackageReference Include="Topshelf" Version="4.1.0" />
<PackageReference Include="Topshelf.NLog" Version="4.1.0" />
Edit: Adding some more implementation code
From App.config:
<configSections>
<section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog" />
<section name="loggly" type="Loggly.Config.LogglyAppConfig, Loggly.Config, Version=3.5.0.0, Culture=neutral, PublicKeyToken=null" />
</configSections>
...
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
autoReload="true"
throwExceptions="false"
internalLogLevel="Trace" internalLogFile="c:\temp\nlog-internal.log">
<extensions>
<add assembly="NLog.Targets.Loggly" />
</extensions>
<variable name="DefaultLayout" value="${longdate} | ${level:uppercase=true:padding=5} | ${message} | ${exception:format=#} | ${callsite} | ${callsite-linenumber} | ${all-event-properties}" />
<targets async="true">
<target xsi:type="File" name="logfile" fileName="logs/log.txt" layout="${DefaultLayout}"/>
<target xsi:type="Console" name="console" layout="${DefaultLayout}"/>
<target xsi:type="Loggly" name="Loggly" layout="${DefaultLayout}"/>
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="logfile,Loggly,console" />
</rules>
</nlog>
<loggly
xmlns="Loggly"
applicationName="masked"
customerToken="masked"
throwExceptions="true">
<transport logTransport="Https"/>
<tags>
<simple>
<tag value="DEV"/>
</simple>
<complex>
<tag type="Loggly.HostnameTag" formatter="{0}"/>
<tag type="Loggly.ApplicationNameTag" formatter="{0}"/>
</complex>
</tags>
</loggly>
Internal log appears to be showing that there is at least attempts to hit Loggly.
2018-12-12 09:05:09.8816 Debug Targets for
Topshelf.Hosts.ConsoleRunHost by level: 2018-12-12 09:05:09.8816 Debug
Trace => 2018-12-12 09:05:09.8816 Debug Debug => 2018-12-12
09:05:09.8816 Debug Info => 2018-12-12 09:05:09.8816 Debug Warn =>
2018-12-12 09:05:09.8816 Debug Error => 2018-12-12 09:05:09.8816 Debug
Fatal => 2018-12-12 09:05:10.1486 Trace AsyncWrapper(Name=Loggly):
Writing 1 events (Timer) 2018-12-12 09:05:10.1486 Trace
AsyncWrapper(Name=console): Writing 1 events (Timer) 2018-12-12
09:05:10.1486 Trace AsyncWrapper(Name=logfile): Writing 1 events
(Timer) 2018-12-12 09:05:10.1747 Trace Opening
Thanks to the comment by #RolfKristensen, I tried to manually configure the csharp client via code and it's working fine. I'll have to move around the configs to the appSettings and have configuration manager pick those up. I guess loggly-csharp needs better documentation around NOT using their config samples in .NET Core.
Is there any way to apply a web.config transform on more than one level? E.g:
web.config
- web.release.config
- web.prod1.config
- web.prod2.config
When targeting prod1, I would like to do a 3 way merge web.config < web.release.config < web.prod1.config. Is this possible?
There is a way to accomplish this. As you don't specify too much, I'm not sure this will satisfy your requirements though. The following is how it could be accomplished from scratch, but you could just pull the bits that you need directly into the csproj you already have.
Create a .csproj file:
Transform.csproj
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<None Include="Web.config" />
<None Include="Web.Debug.config">
<DependentUpon>Web.config</DependentUpon>
</None>
<None Include="Web.Prod.config">
<DependentUpon>Web.config</DependentUpon>
</None>
<None Include="Web.Release.config">
<DependentUpon>Web.config</DependentUpon>
</None>
</ItemGroup>
<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll"/>
<Target Name="TransformRelease">
<TransformXml Source="Web.config"
Transform="Web.Release.config"
Destination="Web.New.config"/>
</Target>
<Target Name="TransformProd">
<TransformXml Source="Web.New.config"
Transform="Web.Prod.config"
Destination="Web.New.config"/>
</Target>
</Project>
Then you can execute your two transforms through invoking an msbuild command from the command line. I used the following powershell commands.
.\msbuild.exe "PATH_TO_YOUR_CSPROJ\Transform.csproj" /t:TransformRelease
.\msbuild.exe "PATH_TO_YOUR_CSPROJ\Transform.csproj" /t:TransformProd
This will transform your web.config using the transforms in the web.release.config and create a new file with the result of that transform web.new.config. Then the second command will transform the web.new.config using the transforms in web.prod.config and update the web.new.config with that transformed value.
Web.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="web" value="web" />
<add key="release" value="web" />
<add key="prod" value="web" />
<add key="release:prod" value="web" />
</appSettings>
</configuration>
Web.Release.config
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<appSettings>
<add key="release" value="release" xdt:Transform="SetAttributes" xdt:Locator="Match(key)" />
<add key="release:prod" value="release" xdt:Transform="SetAttributes" xdt:Locator="Match(key)" />
</appSettings>
</configuration>
Web.Prod.config
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<appSettings>
<add key="prod" value="prod" xdt:Transform="SetAttributes" xdt:Locator="Match(key)" />
<add key="release:prod" value="prod" xdt:Transform="SetAttributes" xdt:Locator="Match(key)" />
</appSettings>
</configuration>
Running the above commands produced Web.New.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="web" value="web" />
<add key="release" value="release" />
<add key="prod" value="prod" />
<add key="release:prod" value="prod" />
</appSettings>
</configuration>
UPDATE
While the above works, I wouldn't want to use it in that manner. After tinkering around with the .csproj a bit, I came up with this which will do the transformation for you in the BeforeBuild task.
<Target Name="TransformRelease">
<TransformXml Source="Web.config" Transform="Web.Release.config" Destination="Web.New.config" />
</Target>
<Target Name="TransformProd" Condition="'$(Configuration)' == 'Release'">
<TransformXml Source="Web.New.config" Transform="Web.Prod.config" Destination="Web.New.config" />
</Target>
<Target Name="BeforeBuild">
<MSBuild Projects="WebApplication1.csproj" Targets="TransformRelease;TransformProd"/>
</Target>
With these defined in your .csproj file, when you build the project as is, it will apply the Release transform alone. When you build the project in the Release configuration, it will apply both the Release and Prod transformations. Obviously you will need to tweak it for your needs given prod1, prod2, etc.
Is not possible out of the box with simple commands, but you can do custom transformation and string replacement using build tasks
A while ago I asked a similar questions and I got a really nice answer using build tasks transformation. Instead of copying it here, take a look in the solution and adapt to your needs.:
Service Fabric Default Publish Profile other than Local.xml
Please see the DOS command below:
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe WindowsFormsApplication1.csproj /t:Test
The .csproj file looks like this:
<PropertyGroup>
<NUnit3-ToolPath>C:\Development\C#\UnitTests\UnitTests\packages\NUnit.ConsoleRunner.3.6.1\tools</NUnit3-ToolPath>
</PropertyGroup>
<Target Name="Test">
<NUnit3 ToolPath="$(NUnit3-ToolPath)" Assemblies="C:\Development\C#\UnitTests\WindowsFormsApplication1\bin\Debug\UnitTests.dll" OutputXmlFile="test-results.xml" />
</Target>
If I exclude: /t:Test from the DOS command then the build succeeds even if the unit tests fail. How do I ensure the unit tests are run when this command is run:
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe WindowsFormsApplication1.csproj
The answer was just to add an AfterTargets attribute as follows:
<Target Name="Test" **AfterTargets="Build">**
<NUnit3 ToolPath="$(NUnit3-ToolPath)" Assemblies="C:\Development\C#\UnitTests\WindowsFormsApplication1\bin\Debug\UnitTests.dll" OutputXmlFile="test-results.xml" /> </Target>