Skip to content

Commit 9e51654

Browse files
authored
Common attributes API (#647)
1 parent f8a0b6c commit 9e51654

File tree

323 files changed

+10160
-532
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

323 files changed

+10160
-532
lines changed

.github/workflows/test.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ jobs:
7171
--no-restore\
7272
--no-build\
7373
--configuration ${{ env.BUILD_CONFIGURATION }}
74+
dotnet run --project ./tests/Allure.Xunit.Tests\
75+
--no-restore\
76+
--no-build\
77+
--configuration ${{ env.BUILD_CONFIGURATION }}
7478
dotnet test ./tests/Allure.SpecFlow.Tests\
7579
--no-restore\
7680
--no-build\

allure-csharp.slnx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@
8686
<Project Path="tests/Allure.Testing/Allure.Testing.csproj">
8787
<Build Solution="Publish|*" Project="false" />
8888
</Project>
89+
<Project Path="tests/Allure.Xunit.Tests/Allure.Xunit.Tests.csproj">
90+
<Build Solution="Publish|*" Project="false" />
91+
</Project>
8992
</Folder>
9093
<Folder Name="/Solution Items/">
9194
<File Path=".gitignore" />

src/Allure.NUnit/Core/AllureNUnitHelper.cs

Lines changed: 85 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44
using System.Linq;
55
using System.Text;
66
using Allure.Net.Commons;
7+
using Allure.Net.Commons.Attributes;
78
using Allure.Net.Commons.Functions;
9+
using Allure.Net.Commons.Sdk;
810
using Allure.Net.Commons.TestPlan;
9-
using Allure.NUnit.Attributes;
1011
using NUnit.Framework;
1112
using NUnit.Framework.Interfaces;
1213
using NUnit.Framework.Internal;
@@ -120,7 +121,8 @@ internal static TestResult CreateTestResult(ITest test)
120121
..ModelFunctions.EnumerateGlobalLabels(),
121122
]
122123
};
123-
UpdateTestDataFromAllureAttributes(test, testResult);
124+
ApplyLegacyAllureAttributes(test, testResult);
125+
ApplyAllureAttributes(test, testResult);
124126
AddTestParametersFromNUnit(test, testResult);
125127
SetIdentifiers(test, testResult);
126128
return testResult;
@@ -262,40 +264,37 @@ static void SetLegacyIdentifiers(ITest test, TestResult testResult)
262264

263265
static void AddTestParametersFromNUnit(ITest test, TestResult testResult)
264266
{
265-
var arguments = CollectNUnitArguments(test);
267+
var parameters = test.Method.MethodInfo.GetParameters();
268+
var arguments = test.Arguments;
266269
var formatters = AllureLifecycle.TypeFormatters;
267-
foreach (var (name, value) in arguments)
268-
{
269-
testResult.parameters.Add(new()
270-
{
271-
name = name,
272-
value = FormatFunctions.Format(value, formatters)
273-
});
274-
}
270+
271+
testResult.parameters.AddRange(
272+
ModelFunctions.CreateParameters(parameters, arguments, formatters)
273+
);
275274
}
276275

277-
static IEnumerable<(string, object)> CollectNUnitArguments(ITest test) =>
278-
test.Method.MethodInfo.GetParameters()
279-
.Select(p => p.Name)
280-
.Zip(
281-
test.Arguments,
282-
(n, v) => (n, v)
283-
);
276+
static void ApplyAllureAttributes(ITest test, TestResult testResult)
277+
{
278+
var testFixtureClass = GetTestFixture(test).TypeInfo.Type;
279+
280+
AllureApiAttribute.ApplyTypeAttributes(testFixtureClass, testResult);
281+
AllureApiAttribute.ApplyMethodAttributes(test.Method.MethodInfo, testResult);
282+
}
284283

285-
static void UpdateTestDataFromAllureAttributes(ITest test, TestResult testResult)
284+
static void ApplyLegacyAllureAttributes(ITest test, TestResult testResult)
286285
{
287286
foreach (var attribute in IterateAllAllureAttribites(test))
288287
{
289288
attribute.UpdateTestResult(testResult);
290289
}
291290
}
292291

293-
static IEnumerable<AllureTestCaseAttribute> IterateAllAllureAttribites(ITest test) =>
292+
static IEnumerable<Attributes.AllureTestCaseAttribute> IterateAllAllureAttribites(ITest test) =>
294293
test.Method
295-
.GetCustomAttributes<AllureTestCaseAttribute>(true)
294+
.GetCustomAttributes<Attributes.AllureTestCaseAttribute>(true)
296295
.Concat(
297296
GetTestFixture(test)
298-
.GetCustomAttributes<AllureTestCaseAttribute>(true)
297+
.GetCustomAttributes<Attributes.AllureTestCaseAttribute>(true)
299298
);
300299

301300
static string GetNamespace(string classFullName)
@@ -309,6 +308,14 @@ static string GetNamespace(string classFullName)
309308
);
310309
}
311310

311+
static string ResolveSubSuite(TestFixture testFixture)
312+
=> AllureApiAttribute
313+
.GetTypeAttributes(testFixture.TypeInfo.Type)
314+
.OfType<AllureNameAttribute>()
315+
.LastOrDefault()
316+
?.Name
317+
?? GetClassName(testFixture.FullName);
318+
312319
static string GetClassName(string classFullName)
313320
{
314321
var lastDotIndex = StripTypeArgs(classFullName)?.LastIndexOf('.') ?? -1;
@@ -349,39 +356,66 @@ static TestFixture GetTestFixture(ITest test)
349356

350357
internal static void ApplyDefaultSuiteHierarchy(ITest test)
351358
{
352-
var testClassFullName = GetTestFixture(test).FullName;
359+
var testFixture = GetTestFixture(test);
360+
var testClassFullName = testFixture.FullName;
353361
var assemblyName = test.TypeInfo?.Assembly?.GetName().Name;
354362
var @namespace = GetNamespace(testClassFullName);
355-
var className = GetClassName(testClassFullName);
363+
var subSuite = ResolveSubSuite(testFixture);
356364

357365
AllureLifecycle.UpdateTestCase(
358366
testResult => ModelFunctions.EnsureSuites(
359367
testResult,
360368
assemblyName,
361369
@namespace,
362-
className
370+
subSuite
363371
)
364372
);
365373
}
366374

367375
private void UpdateTestDataFromNUnitProperties()
368376
{
369-
foreach (var p in GetTestProperties(PropertyNames.Description))
377+
this.ApplyNUnitDescriptions();
378+
this.ApplyNUnitAuthors();
379+
this.ApplyNUnitCategories();
380+
}
381+
382+
void ApplyNUnitDescriptions()
383+
{
384+
bool hasDescription = false;
385+
AllureLifecycle.UpdateTestCase((tr) =>
370386
{
371-
AllureLifecycle.UpdateTestCase(x => x.description += $"{p}\n"
372-
);
387+
hasDescription = tr.description is not null || tr.descriptionHtml is not null;
388+
});
389+
390+
if (hasDescription)
391+
{
392+
// If a description is provided via the Allure API,
393+
// NUnit descriptions are ignored.
394+
return;
373395
}
374396

375-
foreach (var p in GetTestProperties(PropertyNames.Author))
397+
foreach (var p in EnumerateTestProperties(PropertyNames.Description))
376398
{
377-
AllureLifecycle.UpdateTestCase(x => x.labels.Add(Label.Owner(p))
378-
);
399+
AllureLifecycle.UpdateTestCase(x =>
400+
x.description = string.IsNullOrEmpty(x.description)
401+
? p
402+
: $"{x.description}\n\n{p}");
379403
}
404+
}
380405

381-
foreach (var p in GetTestProperties(PropertyNames.Category))
406+
void ApplyNUnitAuthors()
407+
{
408+
foreach (var p in EnumerateTestProperties(PropertyNames.Author))
382409
{
383-
AllureLifecycle.UpdateTestCase(x => x.labels.Add(Label.Tag(p))
384-
);
410+
AllureLifecycle.UpdateTestCase(x => x.labels.Add(Label.Owner(p)));
411+
}
412+
}
413+
414+
void ApplyNUnitCategories()
415+
{
416+
foreach (var p in EnumerateTestProperties(PropertyNames.Category))
417+
{
418+
AllureLifecycle.UpdateTestCase(x => x.labels.Add(Label.Tag(p)));
385419
}
386420
}
387421

@@ -402,30 +436,32 @@ private void AddConsoleOutputAttachment()
402436
}
403437
}
404438

405-
private IEnumerable<string> GetTestProperties(string name)
439+
IEnumerable<string> EnumerateTestProperties(string name)
406440
{
407-
var list = new List<string>();
408-
var currentTest = _test;
409-
while (currentTest.GetType() != typeof(TestSuite)
410-
&& currentTest.GetType() != typeof(TestAssembly))
441+
var propertyContainers = EnumeratePropertyContainers().Reverse();
442+
foreach (var obj in propertyContainers)
411443
{
412-
if (currentTest.Properties.ContainsKey(name))
444+
if (obj.Properties.ContainsKey(name))
413445
{
414-
if (currentTest.Properties[name].Count > 0)
446+
for (var i = 0; i < obj.Properties[name].Count; i++)
415447
{
416-
for (var i = 0; i < currentTest.Properties[name].Count; i++)
417-
{
418-
list.Add(
419-
currentTest.Properties[name][i].ToString()
420-
);
421-
}
448+
yield return obj.Properties[name][i].ToString();
422449
}
423450
}
451+
}
452+
}
424453

425-
currentTest = currentTest.Parent;
454+
IEnumerable<ITest> EnumeratePropertyContainers()
455+
{
456+
for (var test = this._test; ShouldContinue(test); test = test.Parent)
457+
{
458+
yield return test;
426459
}
427460

428-
return list;
461+
static bool ShouldContinue(ITest test)
462+
=> test is not null
463+
&& test.GetType() != typeof(TestSuite)
464+
&& test.GetType() != typeof(TestAssembly);
429465
}
430466

431467
private string ContainerId => $"tc-{_test.Id}";

src/Allure.NUnit/README.md

Lines changed: 149 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,155 @@ Some examples are available [here](https://github.com/allure-framework/allure-cs
3232

3333
## Notes
3434

35-
### Namespace changed to Allure.NUnit
35+
### New in 2.15.0: the common Attribute API
36+
37+
Use the attributes in `Allure.Net.Commons.Attributes` instead of `Allure.NUnit.Attributes`. Read more details [here](https://github.com/allure-framework/allure-csharp/pull/647).
38+
39+
In most cases, the migration is as simple as swapping the using directive:
40+
41+
```diff
42+
- using Allure.NUnit.Attributes;
43+
+ using Allure.Net.Commons.Attributes;
44+
using Allure.NUnit;
45+
using NUnit.Framework;
46+
47+
[AllureFeature("My feature")]
48+
class MyTestClass
49+
{
50+
[AllureStory("My story")]
51+
[Test]
52+
public void MyTestMethod()
53+
{
54+
55+
}
56+
}
57+
```
58+
59+
In some cases, the usage must be updated. They are listed below.
60+
61+
#### `[AllureDescription]`
62+
63+
Set `Append` to keep the concatenation behavior:
64+
65+
```diff
66+
- using Allure.NUnit.Attributes;
67+
+ using Allure.Net.Commons.Attributes;
68+
using NUnit.Framework;
69+
70+
-[AllureDescription("First description")]
71+
+[AllureDescription("First description", Append = true)]
72+
class MyTestClass
73+
{
74+
- [AllureDescription("Second description")]
75+
+ [AllureDescription("Second description", Append = true)]
76+
[Test]
77+
public void MyTestMethod()
78+
{
79+
80+
}
81+
}
82+
```
83+
84+
Use `[AllureDescriptionHtml]` instead of setting `Html`:
85+
86+
```diff
87+
- using Allure.NUnit.Attributes;
88+
+ using Allure.Net.Commons.Attributes;
89+
using NUnit.Framework;
90+
91+
class MyTestClass
92+
{
93+
- [AllureDescription("<p>Html text</p>", Html = true)]
94+
+ [AllureDescriptionHtml("<p>Html text</p>")]
95+
[Test]
96+
public void MyTestMethod()
97+
{
98+
}
99+
}
100+
```
101+
102+
#### `[AllureFeature]`, `[AllureStory]` with multiple values
103+
104+
Use multiple `[AllureFeature]` or `[AllureStory]` attributes instead:
105+
106+
```diff
107+
- using Allure.NUnit.Attributes;
108+
+ using Allure.Net.Commons.Attributes;
109+
110+
-[AllureFeature("Feature 1", "Feature 2")]
111+
+[AllureFeature("Feature 1")]
112+
+[AllureFeature("Feature 2")]
113+
-[AllureStory("Story 1", "Story 2")]
114+
+[AllureStory("Story 1")]
115+
+[AllureStory("Story 2")]
116+
class MyTestClass
117+
{
118+
}
119+
```
120+
121+
#### `[AllureLink]`, `[AllureIssue]`, `[AllureTms]`
122+
123+
Pass the URL or ID as the only positional argument. Use the `Title` property to pass the display
124+
text. Also, use `[AllureTmsItem]` instead of `[AllureTms]`:
125+
126+
```diff
127+
- using Allure.NUnit.Attributes;
128+
+ using Allure.Net.Commons.Attributes;
129+
130+
-[AllureLink("Homepage", "https://allurereport.org")]
131+
+[AllureLink("https://allurereport.org", Title = "Homepage")]
132+
-[AllureIssue("ISSUE-123", "123")]
133+
+[AllureIssue("123", Title = "ISSUE-123")]
134+
-[AllureTms("TMS-345", "345")]
135+
+[AllureTmsItem("345", Title = "TMS-345")]
136+
class MyTestClass
137+
{
138+
}
139+
```
140+
141+
#### `[AllureSeverity]`
142+
143+
Always pass an explicit value as the argument:
144+
145+
```diff
146+
- using Allure.NUnit.Attributes;
147+
+ using Allure.Net.Commons.Attributes;
148+
149+
-[AllureSeverity]
150+
+[AllureSeverity(SeverityLevel.normal)]
151+
class MyTestClass
152+
{
153+
}
154+
```
155+
156+
#### `[Name]` and `[Skip]`
157+
158+
Use `[AllureParameter]` with `Name` and `Ignore` correspondingly:
159+
160+
```diff
161+
- using Allure.NUnit.Attributes;
162+
+ using Allure.Net.Commons.Attributes;
163+
164+
class MyTestClass
165+
{
166+
[AllureStep]
167+
public void MyStepMethod(
168+
- [Name("Foo")] int parameter1,
169+
+ [AllureParameter(Name = "Foo")] int parameter1,
170+
- [Skip] int parameter2
171+
+ [AllureParameter(Ignore = true)] int parameter2
172+
)
173+
{
174+
}
175+
}
176+
```
177+
178+
#### Deprecation notice
179+
180+
Attributes from the `Allure.NUnit.Attributes` namespace will be deprecated in one of the future
181+
releases. Please, migrate to `Allure.Net.Commons.Attributes`.
182+
183+
### New in 2.12.0: Namespace changed to Allure.NUnit
36184

37185
Starting from 2.12.0, the namespace `NUnit.Allure` is deprecated. The API in
38186
that namespace still works, but it will be removed in the future. Please use

0 commit comments

Comments
 (0)