Skip to content

Commit 196523b

Browse files
authored
Steps for Allure.XUnit (#273)
- Adds Allure Steps functionality
1 parent b52349a commit 196523b

17 files changed

Lines changed: 340 additions & 10 deletions
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
using System.Threading.Tasks;
2+
using Allure.Xunit.Attributes;
3+
using Allure.XUnit.Attributes.Steps;
4+
using Xunit;
5+
6+
namespace Allure.XUnit.Examples;
7+
8+
public class ExampleStepAttributes : IAsyncLifetime
9+
{
10+
[AllureBefore("Initialization")]
11+
public Task InitializeAsync()
12+
{
13+
NestedStep(1);
14+
NestedStepReturningString("Second");
15+
return Task.CompletedTask;
16+
}
17+
18+
[AllureXunit]
19+
public async Task Test()
20+
{
21+
WriteHelloStep(42, 4242, "secret");
22+
SomeStep();
23+
await AddAttachmentAsync();
24+
SomeStep();
25+
}
26+
27+
[AllureStep("Write Hello")]
28+
private void WriteHelloStep(int parameter, [Name("value")] int renameMe, [Skip] string password)
29+
{
30+
AddAttachment();
31+
NestedStepReturningString("Write hello nested step");
32+
}
33+
34+
[AllureStep("Add Attachment asynchronously")]
35+
private async Task AddAttachmentAsync()
36+
{
37+
await AllureAttachments.Text("large json", "{}");
38+
}
39+
40+
[AllureStep("Add Attachment")]
41+
private void AddAttachment()
42+
{
43+
AllureAttachments.Text("large json", "{}").GetAwaiter().GetResult();
44+
}
45+
46+
[AllureStep("Another nested step with \"{input}\"")]
47+
private string NestedStepReturningString([Name("Input text parameter")] string input)
48+
{
49+
Assert.True(true);
50+
return input;
51+
}
52+
53+
[AllureStep("Nested step {i}")]
54+
private void NestedStep([Name("number")] int i, [Skip] bool skippedBoolean = true)
55+
{
56+
Assert.Equal(i, i);
57+
}
58+
59+
[AllureStep("Some step")]
60+
private void SomeStep()
61+
{
62+
Assert.True(true);
63+
}
64+
65+
[AllureAfter("Cleanup")]
66+
public Task DisposeAsync()
67+
{
68+
NestedStepReturningString("Cleanup nested 1");
69+
NestedStep(2);
70+
71+
AllureMessageBus.TestOutputHelper.Value.WriteLine("Hello from dispose");
72+
return Task.CompletedTask;
73+
}
74+
}

Allure.XUnit.Examples/ExampleSteps.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
using System;
22
using System.Threading.Tasks;
3-
using Allure.XUnit;
3+
using Allure.Xunit;
44
using Allure.Xunit.Attributes;
55
using Xunit;
66

7-
namespace Allure.Xunit.Examples;
7+
namespace Allure.XUnit.Examples;
88

9+
[Obsolete("See ExampleStepAttributes")]
910
public class ExampleSteps : IAsyncLifetime
1011
{
1112
public Task InitializeAsync()
@@ -25,14 +26,14 @@ public Task DisposeAsync()
2526
return Task.CompletedTask;
2627
}
2728

28-
[AllureXunit]
29+
[AllureXunit(Skip = "ExampleSteps is obsolete")]
2930
public async Task TestParameters()
3031
{
3132
WriteHello(42, 4242, "secret");
3233
await AddAttachment();
3334
}
3435

35-
[AllureXunit]
36+
[AllureXunit(Skip = "ExampleSteps is obsolete")]
3637
public void TestFail()
3738
{
3839
using (new AllureStep("Test Fail"))

Allure.XUnit/Allure.XUnit.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
</PropertyGroup>
1616

1717
<ItemGroup>
18+
<PackageReference Include="AspectInjector" Version="2.7.3" />
19+
<PackageReference Include="xunit.assert" Version="2.4.1" />
1820
<PackageReference Include="xunit.core" Version="2.4.1" />
1921
<PackageReference Include="xunit.runner.reporters" Version="2.4.1" />
2022
</ItemGroup>

Allure.XUnit/AllureAfter.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
using System;
12
using Allure.Net.Commons;
23

34
namespace Allure.Xunit
45
{
56
public sealed class AllureAfter : AllureStepBase<AllureAfter>
67
{
8+
[Obsolete("Use AllureAfterAttribute")]
79
public AllureAfter(string name) : base(Init(name))
810
{
911
}

Allure.XUnit/AllureAttachments.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public static async Task File(string attachmentName, string fileName)
3131
public static async Task AddAttachment(string name, string type, byte[] content, string fileExtension)
3232
{
3333
var source = $"{Guid.NewGuid():N}{AllureConstants.ATTACHMENT_FILE_SUFFIX}{fileExtension}";
34-
await System.IO.File.WriteAllBytesAsync(Path.Combine(AllureLifecycle.Instance.ResultsDirectory, source),
34+
var task = System.IO.File.WriteAllBytesAsync(Path.Combine(AllureLifecycle.Instance.ResultsDirectory, source),
3535
content);
3636
var attachments = Steps.Current.attachments ??= new();
3737
attachments.Add(new()
@@ -40,6 +40,7 @@ await System.IO.File.WriteAllBytesAsync(Path.Combine(AllureLifecycle.Instance.Re
4040
type = type,
4141
source = source
4242
});
43+
await task;
4344
}
4445
}
4546
}

Allure.XUnit/AllureBefore.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
using System;
12
using Allure.Net.Commons;
23

34
namespace Allure.Xunit
45
{
56
public sealed class AllureBefore : AllureStepBase<AllureBefore>
67
{
8+
[Obsolete("Use AllureBeforeAttribute")]
79
public AllureBefore(string name) : base(Init(name))
810
{
911
}

Allure.XUnit/AllureStep.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
using System;
12
using Allure.Net.Commons;
23

34
namespace Allure.Xunit
45
{
56
public sealed class AllureStep : AllureStepBase<AllureStep>
67
{
8+
[Obsolete("Use AllureStepAttribute")]
79
public AllureStep(string name) : base(Init(name))
810
{
911
}

Allure.XUnit/AllureStepAspect.cs

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
using AspectInjector.Broker;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using Allure.Net.Commons;
6+
using Xunit.Sdk;
7+
using System.Reflection;
8+
using System.Threading.Tasks;
9+
using Allure.Xunit;
10+
using Allure.XUnit.Attributes.Steps;
11+
12+
namespace Allure.XUnit
13+
{
14+
[Aspect(Scope.Global)]
15+
public class AllureStepAspect
16+
{
17+
private static readonly MethodInfo AsyncHandler =
18+
typeof(AllureStepAspect).GetMethod(nameof(WrapAsync), BindingFlags.NonPublic | BindingFlags.Static);
19+
20+
private static readonly MethodInfo SyncHandler =
21+
typeof(AllureStepAspect).GetMethod(nameof(WrapSync), BindingFlags.NonPublic | BindingFlags.Static);
22+
23+
[Advice(Kind.Around)]
24+
public object Around([Argument(Source.Name)] string name,
25+
[Argument(Source.Arguments)] object[] args,
26+
[Argument(Source.Target)] Func<object[], object> target,
27+
[Argument(Source.Metadata)] MethodBase metadata,
28+
[Argument(Source.ReturnType)] Type returnType)
29+
{
30+
object executionResult;
31+
32+
var allureBeforeAttribute = metadata.GetCustomAttribute<AllureBeforeAttribute>();
33+
var allureAfterAttribute = metadata.GetCustomAttribute<AllureAfterAttribute>();
34+
var stepName = metadata.GetCustomAttribute<AllureStepBaseAttribute>().Name ?? name;
35+
36+
if (allureBeforeAttribute != null)
37+
{
38+
Steps.StartBeforeFixture(allureBeforeAttribute.Name ?? name);
39+
}
40+
41+
if (allureAfterAttribute != null)
42+
{
43+
Steps.StartAfterFixture(allureAfterAttribute.Name ?? name);
44+
}
45+
46+
47+
foreach (var parameterInfo in metadata.GetParameters())
48+
{
49+
stepName = stepName?.Replace("{" + parameterInfo.Name + "}",
50+
args[parameterInfo.Position]?.ToString() ?? "null");
51+
}
52+
53+
List<Parameter> stepParameters = metadata.GetParameters()
54+
.Select(x => (
55+
name: x.GetCustomAttribute<NameAttribute>()?.Name ?? x.Name,
56+
skip: x.GetCustomAttribute<SkipAttribute>() != null))
57+
.Zip(args, (parameter, value) => parameter.skip
58+
? null
59+
: new Parameter
60+
{
61+
name = parameter.name,
62+
value = value?.ToString()
63+
})
64+
.Where(x => x != null)
65+
.ToList();
66+
67+
try
68+
{
69+
if (allureBeforeAttribute == null && allureAfterAttribute == null)
70+
{
71+
Steps.StartStep(stepName);
72+
Steps.Current.parameters = stepParameters;
73+
}
74+
75+
if (typeof(Task).IsAssignableFrom(returnType))
76+
{
77+
var syncResultType = returnType.IsConstructedGenericType
78+
? returnType.GenericTypeArguments[0]
79+
: typeof(object);
80+
executionResult = AsyncHandler.MakeGenericMethod(syncResultType)
81+
.Invoke(this, new object[] { target, args });
82+
}
83+
else if (typeof(void).IsAssignableFrom(returnType))
84+
{
85+
executionResult = target(args);
86+
}
87+
else
88+
{
89+
executionResult = SyncHandler.MakeGenericMethod(returnType)
90+
.Invoke(this, new object[] { target, args });
91+
}
92+
93+
Steps.PassStep();
94+
}
95+
catch (Exception e)
96+
{
97+
Steps.Current.statusDetails = new StatusDetails
98+
{
99+
message = e.Message,
100+
trace = e.StackTrace
101+
};
102+
103+
if (e is XunitException)
104+
{
105+
Steps.FailStep();
106+
}
107+
else
108+
{
109+
Steps.BrokeStep();
110+
}
111+
112+
throw;
113+
}
114+
115+
return executionResult;
116+
}
117+
118+
private static T WrapSync<T>(Func<object[], object> target, object[] args)
119+
{
120+
try
121+
{
122+
return (T)target(args);
123+
}
124+
catch (Exception e)
125+
{
126+
return default(T);
127+
}
128+
}
129+
130+
private static async Task<T> WrapAsync<T>(Func<object[], object> target, object[] args)
131+
{
132+
try
133+
{
134+
return await (Task<T>)target(args);
135+
}
136+
catch (Exception e)
137+
{
138+
return default(T);
139+
}
140+
}
141+
}
142+
}

Allure.XUnit/AllureStepBase.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public void Dispose()
3131
Steps.PassStep(ExecutableItem);
3232
}
3333

34+
[Obsolete("For named parameters use NameAttribute; For skipped parameters use SkipAttribute")]
3435
public T SetParameter(string name, object value)
3536
{
3637
var parameters = ExecutableItem.parameters ??= new List<Parameter>();
@@ -39,6 +40,7 @@ public T SetParameter(string name, object value)
3940
}
4041

4142
#if NETCOREAPP3_0_OR_GREATER
43+
[Obsolete("For named parameters use NameAttribute; For skipped parameters use SkipAttribute")]
4244
public T SetParameter(object value, [CallerArgumentExpression("value")] string name = null)
4345
{
4446
return SetParameter(name, value);
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using AspectInjector.Broker;
2+
3+
namespace Allure.XUnit.Attributes.Steps
4+
{
5+
[Injection(typeof(AllureStepAspect))]
6+
public class AllureAfterAttribute : AllureStepBaseAttribute
7+
{
8+
public AllureAfterAttribute(string name = null) : base(name)
9+
{
10+
}
11+
}
12+
}

0 commit comments

Comments
 (0)