Skip to content

Add support for ExecutableAttribute on methods when using the fluent interface #284

@MovGP0

Description

@MovGP0

When using the fluent interface the ExecutableAttributes (Given, When, Then) are not supported.

Here is some example code to illustrate how it might be implemented:

void Main()
{
	var cts = new CancellationTokenSource(TimeSpan.FromSeconds(1));
	var bez = "bez";
	new Foo().Given(s => s.BarAsync("let", bez, cts.Token));
}

public sealed class Foo
{
	[When("foo {0} {1} baz")]
	public ValueTask<int> BarAsync(string let, string bar, CancellationToken cancellationToken)
	{
		return ValueTask.FromResult(0);
	}
}

public static class TestExtensions
{
	private static IEnumerable<Type> GetParentTypes(this Type type)
	{
		yield return type;
		if (type.BaseType == null) yield break;
		
		yield return type.BaseType;
		
		foreach (Type b in type.BaseType.GetParentTypes())
		{
			yield return b;
		}		
	}

	private static object GetValue(this MemberInfo memberInfo, object forObject)
	{
		switch (memberInfo.MemberType)
		{
			case MemberTypes.Field:
				return ((FieldInfo)memberInfo).GetValue(forObject);
			case MemberTypes.Property:
				return ((PropertyInfo)memberInfo).GetValue(forObject);
			default:
				throw new NotImplementedException();
		}
	}

	public static void Given<T>(this T model, Expression<Func<T, ValueTask<int>>> expression)
		where T:class
	{
		var argumentProvider = (expression.Body as MethodCallExpression);
		
		var formatString = argumentProvider
			.Method
			.CustomAttributes
			.Where(cad => cad.AttributeType.GetParentTypes().Any(t => t == typeof(ExecutableAttribute)))
			.Select(c => (ExecutableAttribute)c.Constructor.Invoke(c.ConstructorArguments.Select(c => c.Value).ToArray()))
			.Select(e => e.StepTitle)
			.FirstOrDefault();

		for (int i = 0; i < argumentProvider.Arguments.Count; i++)
		{
			if (!formatString.Contains("{" + i + "}")) continue;
			
			var argumentExpression = argumentProvider.Arguments[i];
			if (argumentExpression.NodeType == ExpressionType.Constant)
			{
				var str = argumentExpression.ToString();
				formatString = formatString.Replace("{" + i + "}", str);
			}
			else if(argumentExpression is MemberExpression me)
			{
				if (me.Expression is ConstantExpression ce)
				{
					var value = me.Member.GetValue(ce.Value)?.ToString();
					formatString = formatString.Replace("{" + i + "}", value);
				}
			}
		}
		
		Console.WriteLine(formatString); // foo "let" bez baz
	}
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    need-more-infoNeeds more information or there a pending questions that need answering.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions