Skip to content

Commit

Permalink
XUnit steps refactoring (#278)
Browse files Browse the repository at this point in the history
* Allure.XUnit steps improvements
* Allure.XUnit added support for broken and skipped tests
* README.md was sliced to several files according to projects
  • Loading branch information
neparij committed Jul 25, 2022
1 parent 9e2991a commit 7177073
Show file tree
Hide file tree
Showing 22 changed files with 675 additions and 391 deletions.
2 changes: 2 additions & 0 deletions Allure.Net.Commons/Allure.Net.Commons.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<Product />
<Description>.NET implementation of Allure java-commons</Description>
<PackageProjectUrl>https://www.nuget.org/packages/Allure.Net.Commons</PackageProjectUrl>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/allure-framework/allure-csharp</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>allure</PackageTags>
Expand All @@ -28,6 +29,7 @@


<ItemGroup>
<None Include="README.md" Pack="true" PackagePath="\"/>
<None Include="./../img/allure-net.png" Pack="true" PackagePath="\" />
<Content Include="allureConfig.Template.json" Pack="true" />
<PackageReference Include="MimeTypesMap" Version="1.0.8" />
Expand Down
22 changes: 22 additions & 0 deletions Allure.Net.Commons/AllureLifecycle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,27 @@ public virtual AllureLifecycle WriteTestContainer(string uuid)

#region Fixture

public virtual AllureLifecycle StartBeforeFixture(string parentUuid, FixtureResult result, out string uuid)
{
uuid = Guid.NewGuid().ToString("N");
StartBeforeFixture(parentUuid, uuid, result);
return this;
}

public virtual AllureLifecycle StartBeforeFixture(string parentUuid, string uuid, FixtureResult result)
{
UpdateTestContainer(parentUuid, container => container.befores.Add(result));
StartFixture(uuid, result);
return this;
}

public virtual AllureLifecycle StartAfterFixture(string parentUuid, FixtureResult result, out string uuid)
{
uuid = Guid.NewGuid().ToString("N");
StartAfterFixture(parentUuid, uuid, result);
return this;
}

public virtual AllureLifecycle StartAfterFixture(string parentUuid, string uuid, FixtureResult result)
{
UpdateTestContainer(parentUuid, container => container.afters.Add(result));
Expand Down Expand Up @@ -192,6 +206,13 @@ public virtual AllureLifecycle WriteTestCase(string uuid)

#region Step

public virtual AllureLifecycle StartStep(StepResult result, out string uuid)
{
uuid = Guid.NewGuid().ToString("N");
StartStep(storage.GetCurrentStep(), uuid, result);
return this;
}

public virtual AllureLifecycle StartStep(string uuid, StepResult result)
{
StartStep(storage.GetCurrentStep(), uuid, result);
Expand Down Expand Up @@ -244,6 +265,7 @@ public virtual AllureLifecycle StopStep()

#region Attachment

// TODO: read file in background thread
public virtual AllureLifecycle AddAttachment(string name, string type, string path)
{
var fileExtension = new FileInfo(path).Extension;
Expand Down
77 changes: 77 additions & 0 deletions Allure.Net.Commons/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
## Allure.Net.Commons [![](http://img.shields.io/nuget/vpre/Allure.Net.Commons.svg?style=flat)](https://www.nuget.org/packages/Allure.Net.Commons)
.Net implementation of [Allure java-commons](https://github.com/allure-framework/allure-java/tree/master/allure-java-commons).

Can be targeted either by legacy .net 4.5+ or .net standard 2.* projects.

Use this library to create custom Allure adapters for .Net test frameworks.

### Configuration
Allure lifecycle is configured via json file with default name `allureConfig.json`. NuGet package installs `allureConfig.Template.json` which you can use as an example. There are 2 ways to specify config file location:
- set ALLURE_CONFIG environment variable to the full path of json config file. This option is preferable for .net core projects which utilize nuget libraries directly from nuget packages folder. See this example of setting it via code: https://github.com/allure-framework/allure-csharp/blob/bdf11bd3e1f41fd1e4a8fd22fa465b90b68e9d3f/Allure.Commons.NetCore.Tests/AllureConfigTests.cs#L13-L15

- place `allureConfig.json` to the location of `Allure.Commons.dll`. This option can be used with .net classic projects which copy all referenced package libraries into binary folder. Do not forget to set 'Copy to Output Directory' property to 'Copy always' or 'Copy if newer' in your test project or set it in .csproj:
```
<ItemGroup>
<None Update="allureConfig.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
```
Allure lifecycle will start with default configuration settings if `allureConfig.json` is not found.

Raw json configuration can be accessed from `AllureLifeCycle.Instance.JsonConfiguration` to extend configuration by adapters. See extension example here: https://github.com/allure-framework/allure-csharp/blob/bdf11bd3e1f41fd1e4a8fd22fa465b90b68e9d3f/Allure.SpecFlowPlugin/PluginHelper.cs#L20-L29


Base configuration params are stored in `AllureLifeCycle.Instance.Configuration`

Allure configuration section is used to setup output directory and link patterns, e.g.:
```
{
"allure": {
"directory": "allure-results", // optional, default value is "allure-results"
"title": "custom run title", // optional
"links": //optional
[
"https://example.org/{link}",
"https://example.org/{issue}",
"https://example.org/{tms}"
]
}
}
```
All
Link pattern placeholders will be replaced with URL value of corresponding link type, e.g.

`link(type: "issue", url: "BUG-01") => https://example.org/BUG-01`

### AllureLifecycle
[AllureLifecycle](https://github.com/allure-framework/allure-csharp/blob/main/Allure.Commons/AllureLifecycle.cs) class provides methods for test engine events processing.

Use `AllureLifecycle.Instance` property to access.

#### Fixture Events
* StartBeforeFixture
* StartAfterFixture
* UpdateFixture
* StopFixture

#### Testcase Events
* StartTestCase
* UpdateTestCase
* StopTestCase
* WriteTestCase

### Step Events
* StartStep
* UpdateStep
* StopStep

#### Attachment Events
* AddAttachment - adds attachment to the current lifecycle executable item
* AddScreenDiff - adds needed artifacts to the test case with given uuid to be used with [screen-diff-plugin](https://github.com/allure-framework/allure2/tree/master/plugins/screen-diff-plugin)

#### Utility Methods
* CleanupResultDirectory - can be used in test run setup to clean old result files

### Troubleshooting
...
32 changes: 30 additions & 2 deletions Allure.Net.Commons/Writer/FileSystemResultsWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ internal class FileSystemResultsWriter : IAllureResultsWriter
private readonly AllureConfiguration configuration;
//private Logger logger = LogManager.GetCurrentClassLogger();

private static readonly object BytesWriterLock = new object();
private readonly string outputDirectory;
private readonly JsonSerializer serializer = new JsonSerializer();

Expand Down Expand Up @@ -44,8 +45,9 @@ public void Write(TestResultContainer testResult)

public void Write(string source, byte[] content)
{
var filePath = Path.Combine(outputDirectory, source);
File.WriteAllBytes(filePath, content);
using var task = new WriteTask { Filepath = Path.Combine(outputDirectory, source), Content = content };
ThreadPool.QueueUserWorkItem(WriteBinary, task);
task.WaitOne();
}

public void CleanUp()
Expand Down Expand Up @@ -77,6 +79,25 @@ protected string Write(object allureObject, string fileSuffix)
return filePath;
}

protected void WriteBinary(object writeTask)
{
if (writeTask is WriteTask task)
{
lock (BytesWriterLock)
{
using (var writer = new BinaryWriter(File.Open(task.Filepath, FileMode.Append, FileAccess.Write)))
{
writer.Write(task.Content);
task.Set();
}
}
}
else
{
throw new ArgumentException("Argument cannot be casted from WriteTask class", nameof(writeTask));
}
}

internal virtual bool HasDirectoryAccess(string directory)
{
var tempFile = Path.Combine(directory, Guid.NewGuid().ToString());
Expand Down Expand Up @@ -104,5 +125,12 @@ private string GetResultsDirectory(string outputDirectory)

return new DirectoryInfo(outputDirectory).FullName;
}

private class WriteTask : EventWaitHandle
{
internal WriteTask() : base(false, EventResetMode.ManualReset) { }
public string Filepath;
public byte[] Content;
}
}
}
6 changes: 4 additions & 2 deletions Allure.SpecFlowPlugin/Allure.SpecFlowPlugin.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

<PropertyGroup>
<TargetFrameworks>net461;netstandard2.0</TargetFrameworks>
<PackageId>Allure.Specflow</PackageId>
<PackageId>Allure.SpecFlow</PackageId>
<Version>2.10-SNAPSHOT</Version>
<Authors>Alexander Bakanov</Authors>
<Company />
<Product />
<Description>Generates Allure report result files for SpecFlow test run</Description>
<PackageProjectUrl>https://github.com/allure-framework/allure-csharp/wiki/SpecFlow-Adapter</PackageProjectUrl>
<PackageProjectUrl>https://www.nuget.org/packages/Allure.SpecFlow</PackageProjectUrl>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageIcon>allure-specflow.png</PackageIcon>
<RepositoryUrl>https://github.com/allure-framework/allure-csharp</RepositoryUrl>
<PackageTags>specflow allure</PackageTags>
Expand All @@ -26,6 +27,7 @@


<ItemGroup>
<None Include="README.md" Pack="true" PackagePath="\"/>
<Content Include="allureConfig.Template.json" Pack="true" />
<None Include="./../img/allure-specflow.png" Pack="true" PackagePath="\" />
</ItemGroup>
Expand Down
146 changes: 146 additions & 0 deletions Allure.SpecFlowPlugin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
## SpecFlow Adapter [![](http://img.shields.io/nuget/vpre/Allure.SpecFlow.svg?style=flat)](https://www.nuget.org/packages/Allure.SpecFlow)
Currently supports [SpecFlow](http://specflow.org/) v2.1 - 3.1.*

See [Allure report](https://allure-secondary.z23.web.core.windows.net/) generated from https://github.com/allure-framework/allure-csharp/tree/main/Allure.Features

Please use corresponding NuGet package version.
### Installation

Install [Allure.SpecFlow](https://www.nuget.org/packages/Allure.SpecFlow) nuget package according to your Specflow version.
### Configuration
For Specflow 3 please add or update the following section in your specflow.json:
```
"stepAssemblies": [
{ "assembly": "Allure.SpecFlowPlugin" }
]
```
The plugin uses Allure.Commons json configuration extended with custom sections.
### Custom host name
In case if you want to customize host name which is displayed in Allure Timeline section, please configure `allure.title` property in json configuraion file.
#### If you use NUnit
Default value for allure.directory in allureConfig.json is "allure-results", default working directory in NUnit 3.* is the working directory of console runner. If you don't want to place allure results into NUnit default working folder please either set absolute path in allure.config or set working directory for NUnit in your test setup, e.g.:
``` csharp
[OneTimeSetUp]
public void Init()
{
Environment.CurrentDirectory = Path.GetDirectoryName(GetType().Assembly.Location);
}
```
### Usage
Just run your SpecFlow scenarios and find `allure-results` folder ready to generate Allure report.

### Features
#### Grouping
You can structure your scenarios in 3 Allure hierarchies using feature and scenario tags.
Please read [report structure](https://docs.qameta.io/allure/latest/#_report_structure) Allure documentation section for additional details. Hierarchies consist of the following levels:

**Suites**
* Parent Suite
* * Suite
* * * Sub Suite

**Behaviors**
* Epic
* * Feature
* * * Story

**Packages**
* Package
* * Class
* * * Method

The plugin uses `allure:grouping` configuration section to parse tags with the regular expression. If the expression contains the group, it will be used as hierarchy level name otherwise entire match will be used. E.g:

`^mytag.*` : any tags starting with `@mytag` will be used for grouping.

`^type:(ui|api)` : `@ui` or `@api` tags from regex pattern will be used for grouping.

Check this [config example](https://github.com/allure-framework/allure-csharp/blob/main/Tests.SpecRun/allureConfig.json) as a starting point.

#### Links
You can add links to your scenarios using tags. Tag and link patterns can be configured in `allureConfig.json`
``` json
{
"allure": {
"links": [
"https://myissuetracker.org/{issue}",
"https://mytestmanagementsystem.org?test={tms}"
]
},
"specflow": {
"links": {
"link": "^link:(.+)",
"issue": "^\\d+",
"tms": "^tms:(\\d+)"
}
}
}
```
The following scenario
``` cucumber
@123456 @tms:42 @link:http://example.org
Scenario: I do like click on links in Allure report
```
will have three links in Allure report:
[123456](https://myissuetracker.org/123456), [42](https://mytestmanagementsystem.org?test=tms-42) and http://example.org.

In case there are links, which are generated during tests, then they can be added in code via AllureLifecycle:
``` c#
AllureLifecycle.UpdateTestCase(testResult.uuid, tc =>
{
tc.links.Add(new Link()
{
name = "Example link",
url = "http://example.com"
});
});
```
This will show for scenario link with Text: Example link; and url: "http://example.com".

#### Severity
You can add Severity for your scenarios using tags. It can be configured in `allureConfig.json`
``` json
"labels": {
"severity": "^(normal|blocker|critical|minor|trivial)"
},
```
The following scenario
``` cucumber
@critical
Scenario: ....
```
will set current scenario severity in Allure report as Blocker

#### Tables conversion
Table arguments in SpecFlow steps can be converted either to step csv-attacments or step parameters in the Allure report. The conversion is configurable in `specflow:stepArguments` config section.
With `specflow:stepArguments:convertToParameters` set to `true` the following table arguments will be represented as parameters:
* one row tables
* two column tables with the headers matching both `specflow:stepArguments:paramNameRegex` and `specflow:stepArguments:paramValueRegex` regular expressions.

<table>
<th>SpecFlow</th>
<th>Allure</th>
<tr>
<td>

![](https://github.com/allure-framework/allure-csharp/blob/main/img/wiki-step-all.PNG)

</td>
<td>

![](https://github.com/allure-framework/allure-csharp/blob/main/img/allure-step-all.PNG)

</td>
</tr>
</table>

#### Attachments
You can add attachments to an Allure report from your step bindings:
```csharp
using Allure.Commons;
...
AllureLifecycle.AddAttachment(path, "Attachment Title");
// or
AllureLifecycle.AddAttachment("Attachment Title", "application/txt", "path");
// where "application/txt" - type of your attachment
```
Loading

0 comments on commit 7177073

Please sign in to comment.