tonerdo / pose

Replace any .NET method (including static and non-virtual) with a delegate
MIT License
1.07k stars 75 forks source link

Shim Asynchronous methods #33

Open ashGHub opened 5 years ago

ashGHub commented 5 years ago

Is there a way to shim asynchronous method?

tonerdo commented 5 years ago

@ashGHub, I believe shimming asynchronous methods should be the same as normal methods. Have you tried this and did it throw an error?

ashGHub commented 5 years ago

I tried the following code

public class UnitTest1
{
    [Fact]
    public void Test1()
    {
        HttpClient httpClient = new HttpClient();

        Shim myClassShim = Shim.Replace(() => httpClient.GetStringAsync(Is.A<string>())).With
             (HttpClient @this) => throw new Exception("TEST EXCEPTION"));

         PoseContext.Isolate(() =>
         {
               var url = "DUMMY URL";

               Func<Task> action = async () => { await httpClient.GetStringAsync(url); };

               action.Should().Throw<Exception>()
                     .WithMessage("TEST EXCEPTION");
         });
     }
}

But the test won't pass the the shim setup for GetStringAsync. It is throwing Message: Pose.Exceptions.InvalidShimSignatureException : Mismatched return types

To reproduce this;

  1. Create an xUnit test project using .net core 2.1.
  2. Add a nuget package FluentAssertions.
  3. Use the above code

FYI:

I tried to replace the shim with

Shim myClassShim = Shim.Replace(() => httpClient.GetStringAsync(Is.A<string>())).With(
                (HttpClient @this) => Task.Factory.StartNew(() => throw new Exception("TEST EXCEPTION")));

Still, it is throwing the same exception.

Thanks

tonerdo commented 5 years ago

Because of how delegates are setup the runtime is interpreting your replacement delegate as void by default. I suggest you try returning a dummy Task<string> value.

If you really need to throw an exception, you can try the following:

Task.Factory.StartNew<string>(() => throw new Exception("TEST EXCEPTION"));
ashGHub commented 5 years ago

I used what you've suggested and this error showed up Message: Pose.Exceptions.InvalidShimSignatureException : Parameters count do not match

SWarnberg commented 5 years ago

It doesn't work for me either. Imagine a test scenario where you're testing a ViewModel that contains an async command, which in turn executes an async call to a component that you want to shim. For me it gives the following exception:

System.Reflection.TargetInvocationException
  HResult=0x80131604
  Message=Exception has been thrown by the target of an invocation.
  Source=System.Private.CoreLib
  StackTrace:
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Delegate.DynamicInvokeImpl(Object[] args)
   at Pose.PoseContext.Isolate(Action entryPoint, Shim[] shims)

Inner Exception 1:
InvalidProgramException: Common Language Runtime detected an invalid program.
dm-conti commented 5 years ago

Sorry, I am trying to mock static method but I want simulate that this call throws a particular exception.

image

dm-conti commented 5 years ago

Hi, I'am new of C# and .NET framework. My solution for static methods is:

[TestMethod] [ExpectedException(typeof(TimeoutException), "Dummy Time Out Exception")] public void Test_MockStaticMethod_MethodMocked_AndThrowTimeoutException() { Func myFunc = delegate () { throw new TimeoutException(); };

        Shim consoleShim = Shim.Replace(() => MyItem.GetStaticSession()).With(myFunc);
        try
        {
            PoseContext.Isolate(() =>
            {
                MyItem.GetStaticSession();
            }, consoleShim);
        }
        catch (Exception ex)
        {
            if (ex.InnerException != null && ex.InnerException is TimeoutException) {
                throw ex.InnerException;
            }
        }
    }
OscarCanek commented 5 years ago

@SWarnberg same... Could you fix it?

MariuszPL55 commented 5 years ago

Have a similar problem, some solution @tonerdo?

var shim = Shim
    .Replace(() => Is.A<IDbConnection>().QueryFirstOrDefaultAsync<string>(
        Is.A<string>(),
        Is.A<object>(),
        Is.A<IDbTransaction>(),
        Is.A<int?>(),
        Is.A<CommandType?>()))
    .With(() => Task.Factory.StartNew(() => ""));
Miista commented 5 months ago

For support for async/await please see https://github.com/Miista/pose/issues/12

cc: @tonerdo @ashGHub @SWarnberg @dm-conti @OscarCanek @MariuszPL55 @alexzaytsev-newsroomly @ydemetriades @qjnz @NecromancerKing @lpatterson14