Skip to content

Commit 38394d9

Browse files
Add parameters and custom name attributes for XUnit (#310)
* Add parameters to allure report * Add allure attributes for names * Add example with random data * Delete redundant attributes. Add TypeExtensions to get readable generic type argument name. Add methods to build name and full name for a test. * Delete redundant file. --------- Co-authored-by: Aleksandr Piskunov <residentman@mail.ru>
1 parent 770e4c6 commit 38394d9

File tree

4 files changed

+169
-11
lines changed

4 files changed

+169
-11
lines changed

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)