I am writing a small wrapper ( MyWrapper
) for use in unit tests. Its purpose is to wrap test code with a try-catch in order to catch one specific exception ( MySpecialException
) and then ignore the test.
Why I do that should not be relevant for this question.
Given the code below , how do I prevent others from passing an Action
and using async like this? Or in other words: How do I force them to use MyWrapper.ExecuteAsync(Func<Task>)
instead?
using System;
using System.Threading.Tasks;
using NUnit.Framework;
namespace PreventAsyncActionLambdaExample
{
[TestFixture]
public class Example
{
[Test]
public async Task ExampleTest()
{
// How do I prevent others from passing an action and using async like this?
// Or in other words: How do I force them to use MyWrapper.ExecuteAsync(Func<Task>) instead?
MyWrapper.Execute(async () =>
{
var cut = new ClassUnderTest();
await cut.DoSomethingAsync();
Assert.Fail("Problem: This line will never be reached");
});
}
}
public static class MyWrapper
{
// This method SHOULD NOT, BUT WILL be used in this example
public static void Execute(Action action)
{
try
{
action();
}
catch (MySpecialException)
{
Assert.Ignore("Ignored due to MySpecialException");
}
}
// This method SHOULD BE USED in this example, BUT WILL NOT be used.
public static async Task ExecuteAsync(Func<Task> func)
{
try
{
await func();
}
catch (MySpecialException)
{
Assert.Ignore("Ignored due to MySpecialException");
}
}
}
public class MySpecialException : Exception
{
// This is another exception in reality which is not relevant for this example
}
public class ClassUnderTest
{
public Task DoSomethingAsync()
{
return Task.Delay(20); // Simulate some work
}
}
}
I am afraid that you can't really prevent this at compile-time but you could write another overload that will be picked-up in this case to tell them that they are supposed to use ExecuteAsync
instead:
public static Task Execute(Func<Task> action)
{
throw new Exception("Please use the ExecuteAsync(Func<Task> func) method instead if you will be passing async lambdas");
}
As mentioned in other answers, I do not think you can prevent it in compile time. However, you can do a hacky workaround and throw an exception. Inspired by this answer . It might not be a good solution, but it could at least make the test fail.
public static bool IsThisAsync(Action action)
{
return action.Method.IsDefined(typeof(AsyncStateMachineAttribute),
false);
}
// This method SHOULD NOT, BUT WILL be used in this example
public static void Execute(Action action)
{
try
{
if (IsThisAsync(action))
{
Console.WriteLine("Is async");
throw new ArgumentException("Action cannot be async.", nameof(action));
}
else
{
Console.WriteLine("Is not async");
}
action();
}
catch (MySpecialException)
{
}
}
And tests:
[TestClass]
public class MyWrapperTests
{
// Will not pass
[TestMethod]
public void ShouldAllowAsyncAction()
{
// This will throw an exception
MyWrapper.Execute(async () =>
{
Assert.IsTrue(true);
await Task.Run(() =>
{
Console.WriteLine("Kind of async");
});
});
}
// Will pass, since ArgumentException is expected.
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldThrowArgumentExceptionWhenAsync()
{
// This will throw an exception. But that's expected.
MyWrapper.Execute(async () =>
{
Assert.IsTrue(true);
await Task.Run(() =>
{
Console.WriteLine("Kind of async");
});
});
}
// Passes
[TestMethod]
public void ShouldAllowSyncAction()
{
MyWrapper.Execute(() =>
{
Assert.IsTrue(true);
});
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.