diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e0449e90..0e1f76c7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ The format is loosely based on [Keep a Changelog](http://keepachangelog.com/en/1 * `AmbiguousMatchException` raised when interface has property indexer besides property in VB. (@mujdatdinc, #1129) * Interface default methods are ignored (@hahn-kev, #972) +* Callback validation too strict when setting up a task's `.Result` property (@stakx, #1132) + ## 4.16.0 (2021-01-16) diff --git a/src/Moq/MethodCall.cs b/src/Moq/MethodCall.cs index 02209c4e4..276674970 100644 --- a/src/Moq/MethodCall.cs +++ b/src/Moq/MethodCall.cs @@ -13,6 +13,8 @@ using Moq.Behaviors; using Moq.Properties; +using TypeNameFormatter; + namespace Moq { internal sealed partial class MethodCall : SetupWithOutParameterSupport @@ -310,7 +312,8 @@ void ValidateCallback(Delegate callback) throw new ArgumentException(Resources.InvalidReturnsCallbackNotADelegateWithReturnType); } - var expectedReturnType = this.Method.ReturnType; + var expectedReturnType = this.Expectation.HasResultExpression(out var awaitable) ? awaitable.ResultType + : this.Method.ReturnType; if (!expectedReturnType.IsAssignableFrom(actualReturnType)) { @@ -321,8 +324,8 @@ void ValidateCallback(Delegate callback) string.Format( CultureInfo.CurrentCulture, Resources.InvalidCallbackReturnTypeMismatch, - expectedReturnType, - actualReturnType)); + expectedReturnType.GetFormattedName(), + actualReturnType.GetFormattedName())); } } } diff --git a/src/Moq/Properties/Resources.Designer.cs b/src/Moq/Properties/Resources.Designer.cs index 7fce6d353..24df7ac44 100644 --- a/src/Moq/Properties/Resources.Designer.cs +++ b/src/Moq/Properties/Resources.Designer.cs @@ -187,7 +187,7 @@ internal static string InvalidCallbackParameterMismatch { } /// - /// Looks up a localized string similar to Invalid callback. Setup on method with return type ({0}) cannot invoke callback with return type ({1}).. + /// Looks up a localized string similar to Invalid callback. Setup on method with return type '{0}' cannot invoke callback with return type '{1}'.. /// internal static string InvalidCallbackReturnTypeMismatch { get { diff --git a/src/Moq/Properties/Resources.resx b/src/Moq/Properties/Resources.resx index 09e0223ca..4ecfacf05 100644 --- a/src/Moq/Properties/Resources.resx +++ b/src/Moq/Properties/Resources.resx @@ -285,7 +285,7 @@ Remember that there's no generics covariance in the CLR, so your object must be Type {0} does not have matching protected member: {1} - Invalid callback. Setup on method with return type ({0}) cannot invoke callback with return type ({1}). + Invalid callback. Setup on method with return type '{0}' cannot invoke callback with return type '{1}'. Invalid callback. Setup on method with {0} parameter(s) cannot invoke callback with different number of parameters ({1}). diff --git a/tests/Moq.Tests/SetupTaskResultFixture.cs b/tests/Moq.Tests/SetupTaskResultFixture.cs index 5b73b1ed9..5950730a9 100644 --- a/tests/Moq.Tests/SetupTaskResultFixture.cs +++ b/tests/Moq.Tests/SetupTaskResultFixture.cs @@ -101,6 +101,15 @@ public async Task Setup__completed_Task__Returns() Assert.Same(Friend, friend); } + [Fact] + public async Task Setup__completed_Task__Returns_callback() + { + var person = new Mock(); + person.Setup(p => p.GetFriendTaskAsync().Result).Returns(() => Friend); + var friend = await person.Object.GetFriendTaskAsync(); + Assert.Same(Friend, friend); + } + [Fact] public async Task Setup__completed_Task__Throws() { @@ -119,6 +128,15 @@ public async Task Setup__completed_ValueTask__Returns() Assert.Same(Friend, friend); } + [Fact] + public async Task Setup__completed_ValueTask__Returns_callback() + { + var person = new Mock(); + person.Setup(p => p.GetFriendValueTaskAsync().Result).Returns(()=> Friend); + var friend = await person.Object.GetFriendValueTaskAsync(); + Assert.Same(Friend, friend); + } + [Fact] public async Task Setup__completed_ValueTask__Throws() {