Skip to content

Commit 7562289

Browse files
committed
Merge remote-tracking branch 'origin/main' into nunit-refactorings-1
2 parents 2d62d65 + 38394d9 commit 7562289

File tree

10 files changed

+241
-14
lines changed

10 files changed

+241
-14
lines changed

Allure.Features/Allure.Features.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<PackageReference Include="Castle.Core" Version="4.4.1" />
1111
<PackageReference Include="NUnit" Version="3.13.2" />
1212
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
13-
<PackageReference Include="SpecFlow" Version="3.9.8" />
13+
<PackageReference Include="SpecFlow" Version="3.9.74" />
1414
<PackageReference Include="SpecFlow.SharedSteps" Version="3.0.7" />
1515
<PackageReference Include="SpecFlow.Tools.MsBuild.Generation" Version="3.9.8" />
1616
<PackageReference Include="SpecFlow.NUnit" Version="3.9.8" />
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System;
2+
using Allure.Net.Commons;
3+
using NUnit.Allure.Attributes;
4+
using NUnit.Framework;
5+
6+
namespace Allure.NUnit.Examples;
7+
8+
[AllureSuite("Tests - Steps")]
9+
public class AllureStepArgumentFormattingTest : BaseTest
10+
{
11+
[AllureStep("Step with params #{0}")]
12+
private static void StepWithCustomClassParams(CustomClass firstParam)
13+
{
14+
Console.WriteLine(firstParam);
15+
}
16+
17+
[OneTimeSetUp]
18+
public static void OneTimeSetUp()
19+
{
20+
AllureLifecycle.Instance.AddTypeFormatter(new CustomClassFormatter());
21+
}
22+
23+
[Test]
24+
[AllureName("Test with custom formatting for step argument")]
25+
public void StepWithCustomParamTest()
26+
{
27+
StepWithCustomClassParams(new CustomClass { I = 10, S = "string" });
28+
}
29+
30+
private class CustomClass
31+
{
32+
public int I { get; init; }
33+
public string S { get; init; }
34+
}
35+
36+
private class CustomClassFormatter : TypeFormatter<CustomClass>
37+
{
38+
public override string Format(CustomClass value) => $"___{value.S} + {value.I}___";
39+
}
40+
}

Allure.Net.Commons/Allure.Net.Commons.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
<None Include="./../img/Allure-Color.png" Pack="true" PackagePath="\" />
3333
<Content Include="allureConfig.Template.json" Pack="true" />
3434
<PackageReference Include="MimeTypesMap" Version="1.0.8" />
35-
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
35+
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
3636
</ItemGroup>
3737

3838
</Project>

Allure.Net.Commons/AllureLifecycle.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using System;
2+
using System.Collections.Generic;
3+
using System.Collections.ObjectModel;
24
using System.IO;
35
using System.Runtime.CompilerServices;
46
using System.Threading;
@@ -15,6 +17,11 @@ namespace Allure.Net.Commons
1517
{
1618
public class AllureLifecycle
1719
{
20+
private readonly Dictionary<Type, ITypeFormatter> typeFormatters = new();
21+
22+
public IReadOnlyDictionary<Type, ITypeFormatter> TypeFormatters =>
23+
new ReadOnlyDictionary<Type, ITypeFormatter>(typeFormatters);
24+
1825
private static readonly object Lockobj = new();
1926
private static AllureLifecycle instance;
2027
private readonly AllureStorage storage;
@@ -60,6 +67,12 @@ public static AllureLifecycle Instance
6067
}
6168
}
6269

70+
public void AddTypeFormatter<T>(TypeFormatter<T> typeFormatter) =>
71+
AddTypeFormatterImpl(typeof(T), typeFormatter);
72+
73+
private void AddTypeFormatterImpl(Type type, ITypeFormatter formatter) =>
74+
typeFormatters[type] = formatter;
75+
6376
#region TestContainer
6477

6578
public virtual AllureLifecycle StartTestContainer(TestResultContainer container)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
namespace Allure.Net.Commons;
2+
3+
public interface ITypeFormatter
4+
{
5+
string Format(object value);
6+
}
7+
8+
public abstract class TypeFormatter<T> : ITypeFormatter
9+
{
10+
public abstract string Format(T value);
11+
12+
string ITypeFormatter.Format(object value)
13+
{
14+
return Format((T)value);
15+
}
16+
}

Allure.XUnit.Examples/Allure.XUnit.Examples.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<ItemGroup>
99
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
1010
<PackageReference Include="xunit" Version="2.4.1" />
11-
<PackageReference Include="coverlet.collector" Version="3.1.2">
11+
<PackageReference Include="coverlet.collector" Version="3.2.0">
1212
<PrivateAssets>all</PrivateAssets>
1313
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1414
</PackageReference>

Allure.XUnit.Examples/ExampleParameterisedTests.cs

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Diagnostics;
23
using System.IO;
34
using Allure.Xunit.Attributes;
45
using Allure.XUnit.Examples.TestData;
@@ -22,10 +23,10 @@ public ExampleParameterisedTests()
2223
public void TestTheoryWithMemberDataProperty(int value1, int value2, int expected)
2324
{
2425
var result = value1 + value2;
25-
26+
2627
Assert.Equal(expected, result);
2728
}
28-
29+
2930
[AllureXunitTheory]
3031
[AllureParentSuite("AllTests")]
3132
[AllureSuite("Test AllureXunitTheory")]
@@ -34,10 +35,10 @@ public void TestTheoryWithMemberDataProperty(int value1, int value2, int expecte
3435
public void TestTheoryWithClassData(int value1, int value2, int expected)
3536
{
3637
var result = value1 + value2;
37-
38+
3839
Assert.Equal(expected, result);
3940
}
40-
41+
4142
[AllureXunitTheory]
4243
[AllureParentSuite("AllTests")]
4344
[AllureSuite("Test AllureXunitTheory")]
@@ -59,5 +60,32 @@ public void TestTheoryWithMemberData(MyTestClass a, MyTestClass b)
5960
{
6061
Assert.Equal(a.Test, b.Test);
6162
}
63+
64+
[AllureXunitTheory]
65+
[AllureParentSuite("AllTests")]
66+
[AllureSuite("Test AllureXunitTheory")]
67+
[AllureSubSuite("Test test MemberData with random data")]
68+
[MemberData(nameof(TestDataGenerators.RandomData), MemberType = typeof(TestDataGenerators))]
69+
public void TestTheoryWithMemberDataThatReturnsRandomData(int value1, int value2, int expected)
70+
{
71+
var result = value1 + value2;
72+
73+
Assert.Equal(expected, result);
74+
}
75+
76+
[AllureXunitTheory]
77+
[AllureParentSuite("AllTests")]
78+
[AllureSuite("Test AllureXunitTheory")]
79+
[AllureSubSuite("Test test with generic arguments")]
80+
[InlineData(5, 10)]
81+
[InlineData(10, 20)]
82+
public void TestTheoryWithInlineDataThatAcceptsGenericArgument(int? value, int? expected)
83+
{
84+
var result = value * 2;
85+
86+
Debug.Assert(expected != null, nameof(expected) + " != null");
87+
Debug.Assert(result != null, nameof(result) + " != null");
88+
Assert.Equal(expected.Value, result.Value);
89+
}
6290
}
63-
}
91+
}

Allure.XUnit.Examples/TestData/TestDataGenerators.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
1+
using System;
12
using System.Collections.Generic;
3+
using System.Linq;
24

35
namespace Allure.XUnit.Examples.TestData
46
{
57
public class TestDataGenerators
68
{
9+
private static readonly object SyncLock = new();
10+
private static readonly Random Random = new();
11+
712
public static IEnumerable<object[]> Data =>
813
new List<object[]>
914
{
@@ -21,5 +26,20 @@ public class TestDataGenerators
2126
new object[] {new MyTestClass {Test = 10}, new MyTestClass {Test = 11}}
2227
};
2328

29+
public static IEnumerable<object[]> RandomData =>
30+
Enumerable.Range(0, 4).Select(_ => CreateTestData());
31+
32+
private static object[] CreateTestData()
33+
{
34+
return new object[] {GetRandomNumber(), GetRandomNumber(), GetRandomNumber()};
35+
}
36+
37+
private static int GetRandomNumber()
38+
{
39+
lock(SyncLock)
40+
{
41+
return Random.Next();
42+
}
43+
}
2444
}
2545
}

Allure.XUnit/AllureXunitHelper.cs

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
using Allure.Net.Commons;
66
using Allure.XUnit;
77
using Allure.Xunit.Attributes;
8+
using Xunit;
89
using Xunit.Abstractions;
9-
using Xunit.Sdk;
1010

1111
namespace Allure.Xunit
1212
{
@@ -49,17 +49,24 @@ public static void StartTestCase(ITestCaseMessage testCaseMessage)
4949
testResults.TestResult = new()
5050
{
5151
uuid = uuid,
52-
name = testCase.DisplayName,
52+
name = BuildName(testCase),
5353
historyId = testCase.DisplayName,
54-
fullName = testCase.DisplayName,
54+
fullName = BuildFullName(testCase),
5555
labels = new()
5656
{
5757
Label.Thread(),
5858
Label.Host(),
5959
Label.TestClass(testCase.TestMethod.TestClass.Class.Name),
6060
Label.TestMethod(testCase.DisplayName),
6161
Label.Package(testCase.TestMethod.TestClass.Class.Name)
62-
}
62+
},
63+
parameters = testCase.TestMethod.Method.GetParameters()
64+
.Zip(testCase.TestMethodArguments ?? Array.Empty<object>(), (param, value) => new Parameter
65+
{
66+
name = param.Name,
67+
value = value?.ToString() ?? "null"
68+
})
69+
.ToList()
6370
};
6471
UpdateTestDataFromAttributes(testResults.TestResult, testCase);
6572
AllureLifecycle.Instance.StartTestCase(testResults.TestResultContainer.uuid, testResults.TestResult);
@@ -224,8 +231,7 @@ private static void UpdateTestDataFromAttributes(TestResult testResult, ITestCas
224231
case AllureDescriptionAttribute descriptionAttribute:
225232
testResult.description = descriptionAttribute.Description;
226233
break;
227-
228-
234+
229235
case AllureIdAttribute allureIdAttribute:
230236
var allureIdLabel = new Label {name = "ALLURE_ID", value = allureIdAttribute.AllureId};
231237
testResult.labels.AddDistinct(allureIdLabel, false);
@@ -242,5 +248,36 @@ private static void UpdateTestDataFromAttributes(TestResult testResult, ITestCas
242248
}
243249
}
244250
}
251+
252+
private static string BuildName(ITestCase testCase)
253+
{
254+
var factAttribute = testCase.TestMethod.Method.GetCustomAttributes(typeof(FactAttribute)).SingleOrDefault();
255+
if (factAttribute is null)
256+
{
257+
return BuildFullName(testCase);
258+
}
259+
260+
var displayName = factAttribute.GetNamedArgument<string>("DisplayName");
261+
if (string.IsNullOrWhiteSpace(displayName))
262+
{
263+
return BuildFullName(testCase);
264+
}
265+
266+
return displayName;
267+
}
268+
269+
private static string BuildFullName(ITestCase testCase)
270+
{
271+
var parameters = testCase.TestMethod.Method
272+
.GetParameters()
273+
.Select(parameter =>
274+
$"{parameter.ParameterType.ToRuntimeType().GetFullFormattedTypeName()} {parameter.Name}")
275+
.ToArray();
276+
var parametersSegment = parameters.Any()
277+
? $"({string.Join(", ", parameters)})"
278+
: string.Empty;
279+
280+
return $"{testCase.TestMethod.TestClass.Class.Name}.{testCase.TestMethod.Method.Name}{parametersSegment}";
281+
}
245282
}
246283
}

Allure.XUnit/TypeExtensions.cs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Reflection;
4+
using System.Text;
5+
6+
namespace Allure.XUnit
7+
{
8+
internal static class TypeExtensions
9+
{
10+
public static string GetFullFormattedTypeName(this Type type, Func<string, string> namingRule = null)
11+
{
12+
namingRule ??= typeName => typeName;
13+
if (!type.IsGenericType)
14+
{
15+
return namingRule.Invoke(type.Name);
16+
}
17+
18+
var nameBuilder = new StringBuilder();
19+
BuildGenericTypeName(type, nameBuilder, namingRule);
20+
return nameBuilder.ToString();
21+
}
22+
23+
private static void BuildGenericTypeName(Type type, StringBuilder nameBuilder, Func<string, string> namingRule)
24+
{
25+
if (!type.IsGenericType)
26+
{
27+
return;
28+
}
29+
30+
StartGenericType(type, nameBuilder, namingRule);
31+
for (var index = 0; index < type.GenericTypeArguments.Length; index++)
32+
{
33+
var genericTypeArgument = type.GenericTypeArguments[index];
34+
if (genericTypeArgument.IsGenericType)
35+
{
36+
BuildGenericTypeName(genericTypeArgument, nameBuilder, namingRule);
37+
AppendDelimiterIfNeeded(type.GenericTypeArguments, nameBuilder, index);
38+
continue;
39+
}
40+
41+
nameBuilder.Append(namingRule.Invoke(genericTypeArgument.Name));
42+
AppendDelimiterIfNeeded(type.GenericTypeArguments, nameBuilder, index);
43+
}
44+
45+
EndGenericType(nameBuilder);
46+
}
47+
48+
private static void StartGenericType(
49+
MemberInfo type,
50+
StringBuilder nameBuilder,
51+
Func<string, string> namingRule)
52+
{
53+
var typeName = namingRule.Invoke(type.Name.Substring(0, type.Name.IndexOf("`", StringComparison.Ordinal)));
54+
nameBuilder.Append(typeName).Append('<');
55+
}
56+
57+
private static void EndGenericType(StringBuilder nameBuilder)
58+
{
59+
nameBuilder.Append('>');
60+
}
61+
62+
private static void AppendDelimiterIfNeeded(
63+
IReadOnlyCollection<Type> genericTypeArguments,
64+
StringBuilder nameBuilder,
65+
int index)
66+
{
67+
if (genericTypeArguments.Count - index != 1)
68+
{
69+
nameBuilder.Append(", ");
70+
}
71+
}
72+
}
73+
}

0 commit comments

Comments
 (0)