From 54e132bced90274c7a7324a63b0b6f12911322c6 Mon Sep 17 00:00:00 2001 From: Martin Hammerschmied Date: Wed, 17 Feb 2016 08:39:45 +0100 Subject: [PATCH 01/13] Allow for class mocks and interface mocks to mock `object.ToString()`, `object.Equals(object obj)` and `object.GetHashCode()`. --- Source/InterceptorStrategies.cs | 13 +++- Source/Proxy/CastleProxyFactory.cs | 13 ++-- Source/Proxy/ProxyGenerationHelpers.cs | 95 ++++++++++++++------------ UnitTests/MockFixture.cs | 62 ++++++++++++++++- 4 files changed, 132 insertions(+), 51 deletions(-) diff --git a/Source/InterceptorStrategies.cs b/Source/InterceptorStrategies.cs index 62983248b..b255b5754 100644 --- a/Source/InterceptorStrategies.cs +++ b/Source/InterceptorStrategies.cs @@ -139,7 +139,8 @@ public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorCo { var method = invocation.Method; - if (method.DeclaringType == typeof(Object) && method.Name == "ToString") + // Only if there is no corresponding setup + if (IsObjectToStringMethod(method) && !ctx.OrderedCalls.Select(c => IsObjectToStringMethod(c.Method)).Any()) { invocation.ReturnValue = ctx.Mock.ToString() + ".Object"; return InterceptionAction.Stop; @@ -147,6 +148,16 @@ public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorCo return InterceptionAction.Continue; } + + protected bool IsObjectToStringMethod(MethodInfo method) + { + if (method.DeclaringType == typeof(object) && method.Name == "ToString") + { + return true; + } + + return false; + } } internal class HandleTracking : IInterceptStrategy diff --git a/Source/Proxy/CastleProxyFactory.cs b/Source/Proxy/CastleProxyFactory.cs index c08ce9113..f3db8e73b 100644 --- a/Source/Proxy/CastleProxyFactory.cs +++ b/Source/Proxy/CastleProxyFactory.cs @@ -71,15 +71,20 @@ static CastleProxyFactory() AttributesToAvoidReplicating.Add(); #endif #endif - proxyOptions = new ProxyGenerationOptions { Hook = new ProxyMethodHook(), BaseTypeForInterfaceProxy = typeof(InterfaceProxy) }; + proxyOptions = new ProxyGenerationOptions { Hook = new ProxyMethodHook() }; } /// public object CreateProxy(Type mockType, ICallInterceptor interceptor, Type[] interfaces, object[] arguments) { - if (mockType.IsInterface) { - return generator.CreateInterfaceProxyWithoutTarget(mockType, interfaces, proxyOptions, new Interceptor(interceptor)); - } + if (mockType.IsInterface) + { + // Add type to additional interfaces and mock System.Object instead. + // This way it is also possible to mock System.Object methods. + Array.Resize(ref interfaces, interfaces.Length + 1); + interfaces[interfaces.Length - 1] = mockType; + mockType = typeof(object); + } try { diff --git a/Source/Proxy/ProxyGenerationHelpers.cs b/Source/Proxy/ProxyGenerationHelpers.cs index bb175f191..928eed404 100644 --- a/Source/Proxy/ProxyGenerationHelpers.cs +++ b/Source/Proxy/ProxyGenerationHelpers.cs @@ -2,53 +2,62 @@ using System.ComponentModel; using System.Reflection; using Castle.DynamicProxy; +using System.Collections.Generic; +using System.Linq; namespace Moq.Proxy { - /// - /// Hook used to tells Castle which methods to proxy in mocked classes. - /// - /// Here we proxy the default methods Castle suggests (everything Object's methods) - /// plus Object.ToString(), so we can give mocks useful default names. - /// - /// This is required to allow Moq to mock ToString on proxy *class* implementations. - /// - internal class ProxyMethodHook : AllMethodsHook - { - /// - /// Extends AllMethodsHook.ShouldInterceptMethod to also intercept Object.ToString(). - /// - public override bool ShouldInterceptMethod(Type type, MethodInfo methodInfo) - { - var isObjectToString = methodInfo.DeclaringType == typeof(Object) && methodInfo.Name == "ToString"; - return base.ShouldInterceptMethod(type, methodInfo) || isObjectToString; - } - } + /// + /// Hook used to tells Castle which methods to proxy in mocked classes. + /// + /// Here we proxy the default methods Castle suggests (everything Object's methods) + /// plus Object.ToString(), so we can give mocks useful default names. + /// + /// This is required to allow Moq to mock ToString on proxy *class* implementations. + /// + internal class ProxyMethodHook : AllMethodsHook + { + protected static readonly Tuple[] GrantedMethods = new Tuple[] + { + Tuple.Create(typeof(object), "ToString"), + Tuple.Create(typeof(object), "Equals"), + Tuple.Create(typeof(object), "GetHashCode") + }; - /// - /// The base class used for all our interface-inheriting proxies, which overrides the default - /// Object.ToString() behavior, to route it via the mock by default, unless overriden by a - /// real implementation. - /// - /// This is required to allow Moq to mock ToString on proxy *interface* implementations. - /// - /// - /// This is internal to Moq and should not be generally used. - /// - /// Unfortunately it must be public, due to cross-assembly visibility issues with reflection, - /// see github.com/Moq/moq4/issues/98 for details. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public abstract class InterfaceProxy - { - /// - /// Overrides the default ToString implementation to instead find the mock for this mock.Object, - /// and return MockName + '.Object' as the mocked object's ToString, to make it easy to relate - /// mocks and mock object instances in error messages. - /// - public override string ToString() + /// + /// Extends AllMethodsHook.ShouldInterceptMethod to also intercept Object.ToString(). + /// + public override bool ShouldInterceptMethod(Type type, MethodInfo methodInfo) { - return ((IMocked)this).Mock.ToString() + ".Object"; - } + bool isGranted = GrantedMethods.Contains(Tuple.Create(methodInfo.DeclaringType, methodInfo.Name)); + return base.ShouldInterceptMethod(type, methodInfo) || isGranted; + } } + + ///// + ///// The base class used for all our interface-inheriting proxies, which overrides the default + ///// Object.ToString() behavior, to route it via the mock by default, unless overriden by a + ///// real implementation. + ///// + ///// This is required to allow Moq to mock ToString on proxy *interface* implementations. + ///// + ///// + ///// This is internal to Moq and should not be generally used. + ///// + ///// Unfortunately it must be public, due to cross-assembly visibility issues with reflection, + ///// see github.com/Moq/moq4/issues/98 for details. + ///// + //[EditorBrowsable(EditorBrowsableState.Never)] + //public abstract class InterfaceProxy + //{ + // /// + // /// Overrides the default ToString implementation to instead find the mock for this mock.Object, + // /// and return MockName + '.Object' as the mocked object's ToString, to make it easy to relate + // /// mocks and mock object instances in error messages. + // /// + // public override string ToString() + // { + // return ((IMocked)this).Mock.ToString() + ".Object"; + // } + // } } diff --git a/UnitTests/MockFixture.cs b/UnitTests/MockFixture.cs index 1f56bd18c..c0b571452 100644 --- a/UnitTests/MockFixture.cs +++ b/UnitTests/MockFixture.cs @@ -254,9 +254,65 @@ public void OverridesBehaviorFromAbstractClass() Assert.False(mock.Object.Check("foo")); Assert.True(mock.Object.Check("bar")); - } - - [Fact] + } + + [Fact] + public void CanSetupToString() + { + var mock = new Mock(); + mock.Setup(x => x.ToString()).Returns("This is me"); + + Assert.Equal("This is me", mock.Object.ToString()); + } + + [Fact] + public void CanSetupToStringForInterface() + { + var mock = new Mock(); + mock.Setup(x => x.ToString()).Returns("This is me"); + + Assert.Equal("This is me", mock.Object.ToString()); + } + + [Fact] + public void CanSetupGetHashCode() + { + var mock = new Mock(); + mock.Setup(x => x.GetHashCode()).Returns(527); + + Assert.Equal(527, mock.Object.GetHashCode()); + } + + [Fact] + public void CanSetupGetHashCodeForInterface() + { + var mock = new Mock(); + mock.Setup(x => x.GetHashCode()).Returns(527); + + Assert.Equal(527, mock.Object.GetHashCode()); + } + + [Fact] + public void CanSetupObjectEquals() + { + var mock = new Mock(); + mock.Setup(x => x.Equals(It.IsAny())).Returns((obj) => false); + var foo = mock.Object; + + Assert.True(!foo.Equals(foo)); + } + + [Fact] + public void CanSetupObjectEqualsForInterface() + { + var mock = new Mock(); + mock.Setup(x => x.Equals(It.IsAny())).Returns((obj) => false); + var foo = mock.Object; + + Assert.True(!foo.Equals(foo)); + } + + [Fact] public void CallsUnderlyingClassEquals() { var mock = new Mock(); From 79ba390c5e934e4bdb9554e957d2eb75f6543d99 Mon Sep 17 00:00:00 2001 From: Martin Hammerschmied Date: Mon, 18 Jul 2016 10:42:41 +0200 Subject: [PATCH 02/13] Made `GrantedMethods` in `ProxyMethodHook` a HashSet for faster access. Also fixed a couple of white space errors. --- Source/InterceptorStrategies.cs | 590 ++++++++++++------------- Source/Proxy/CastleProxyFactory.cs | 34 +- Source/Proxy/ProxyGenerationHelpers.cs | 66 +-- UnitTests/MockFixture.cs | 137 +++--- 4 files changed, 414 insertions(+), 413 deletions(-) diff --git a/Source/InterceptorStrategies.cs b/Source/InterceptorStrategies.cs index b255b5754..e105b0fb3 100644 --- a/Source/InterceptorStrategies.cs +++ b/Source/InterceptorStrategies.cs @@ -7,301 +7,301 @@ namespace Moq { - internal class HandleMockRecursion : IInterceptStrategy - { + internal class HandleMockRecursion : IInterceptStrategy + { public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorContext ctx, CurrentInterceptContext localctx) - { + { if (invocation.Method != null && invocation.Method.ReturnType != null && - invocation.Method.ReturnType != typeof(void)) - { - Mock recursiveMock; - if (ctx.Mock.InnerMocks.TryGetValue(invocation.Method, out recursiveMock)) - { - invocation.ReturnValue = recursiveMock.Object; - } - else - { - invocation.ReturnValue = ctx.Mock.DefaultValueProvider.ProvideDefault(invocation.Method); - } - return InterceptionAction.Stop; - } - return InterceptionAction.Continue; - } - } - - internal class InvokeBase : IInterceptStrategy - { - public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorContext ctx, CurrentInterceptContext localctx) - { - if (invocation.Method.DeclaringType == typeof(object) || // interface proxy - ctx.Mock.ImplementedInterfaces.Contains(invocation.Method.DeclaringType) && !invocation.Method.IsEventAttach() && !invocation.Method.IsEventDetach() && ctx.Mock.CallBase && !ctx.Mock.MockedType.IsInterface || // class proxy with explicitly implemented interfaces. The method's declaring type is the interface and the method couldn't be abstract - invocation.Method.DeclaringType.IsClass && !invocation.Method.IsAbstract && ctx.Mock.CallBase // class proxy - ) - { - // Invoke underlying implementation. - - // For mocked classes, if the target method was not abstract, - // invoke directly. - // Will only get here for Loose behavior. - // TODO: we may want to provide a way to skip this by the user. - invocation.InvokeBase(); - return InterceptionAction.Stop; - } - else - { - return InterceptionAction.Continue; - } - } - } - - internal class ExecuteCall : IInterceptStrategy - { - InterceptorContext ctx; - public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorContext ctx, CurrentInterceptContext localctx) - { - this.ctx = ctx; - IProxyCall currentCall = localctx.Call; - - if (currentCall != null) - { - currentCall.SetOutParameters(invocation); - - // We first execute, as there may be a Throws - // and therefore we might never get to the - // next line. - currentCall.Execute(invocation); - ThrowIfReturnValueRequired(currentCall, invocation); - return InterceptionAction.Stop; - } - else - { - return InterceptionAction.Continue; - } - } - private void ThrowIfReturnValueRequired(IProxyCall call, ICallContext invocation) - { - if (ctx.Behavior != MockBehavior.Loose && - invocation.Method != null && - invocation.Method.ReturnType != null && - invocation.Method.ReturnType != typeof(void)) - { - var methodCall = call as MethodCallReturn; - if (methodCall == null || !methodCall.HasReturnValue) - { - throw new MockException( - MockException.ExceptionReason.ReturnValueRequired, - ctx.Behavior, - invocation); - } - } - } - } - - internal class ExtractProxyCall : IInterceptStrategy - { - - public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorContext ctx, CurrentInterceptContext localctx) - { - localctx.Call = FluentMockContext.IsActive ? (IProxyCall)null : ctx.OrderedCalls.LastOrDefault(c => c.Matches(invocation)); - if (localctx.Call != null) - { - localctx.Call.EvaluatedSuccessfully(); - } - else if (!FluentMockContext.IsActive && ctx.Behavior == MockBehavior.Strict) - { - throw new MockException(MockException.ExceptionReason.NoSetup, ctx.Behavior, invocation); - } - - return InterceptionAction.Continue; - } - - } - - internal class InterceptMockPropertyMixin : IInterceptStrategy - { - public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorContext ctx, CurrentInterceptContext localctx) - { - var method = invocation.Method; - - if (typeof(IMocked).IsAssignableFrom(method.DeclaringType) && method.Name == "get_Mock") - { - invocation.ReturnValue = ctx.Mock; - return InterceptionAction.Stop; - } - - return InterceptionAction.Continue; - } - } - - internal class InterceptToStringMixin : IInterceptStrategy - { - public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorContext ctx, CurrentInterceptContext localctx) - { - var method = invocation.Method; - - // Only if there is no corresponding setup - if (IsObjectToStringMethod(method) && !ctx.OrderedCalls.Select(c => IsObjectToStringMethod(c.Method)).Any()) - { - invocation.ReturnValue = ctx.Mock.ToString() + ".Object"; - return InterceptionAction.Stop; - } - - return InterceptionAction.Continue; - } - - protected bool IsObjectToStringMethod(MethodInfo method) - { - if (method.DeclaringType == typeof(object) && method.Name == "ToString") - { - return true; - } - - return false; - } - } - - internal class HandleTracking : IInterceptStrategy - { - - public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorContext ctx, CurrentInterceptContext localctx) - { - // Track current invocation if we're in "record" mode in a fluent invocation context. - if (FluentMockContext.IsActive) - { - FluentMockContext.Current.Add(ctx.Mock, invocation); - } - return InterceptionAction.Continue; - } - } - - internal class HandleDestructor : IInterceptStrategy - { - public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorContext ctx, CurrentInterceptContext localctx) - { - return invocation.Method.IsDestructor() ? InterceptionAction.Stop : InterceptionAction.Continue; - } - } - - internal class AddActualInvocation : IInterceptStrategy - { - - /// - /// Get an eventInfo for a given event name. Search type ancestors depth first if necessary. - /// - /// Name of the event, with the set_ or get_ prefix already removed - private EventInfo GetEventFromName(string eventName) - { - var depthFirstProgress = new Queue(ctx.Mock.ImplementedInterfaces.Skip(1)); - depthFirstProgress.Enqueue(ctx.TargetType); - while (depthFirstProgress.Count > 0) - { - var currentType = depthFirstProgress.Dequeue(); - var eventInfo = currentType.GetEvent(eventName); - if (eventInfo != null) - { - return eventInfo; - } - - foreach (var implementedType in GetAncestorTypes(currentType)) - { - depthFirstProgress.Enqueue(implementedType); - } - } - return GetNonPublicEventFromName(eventName); - } - - /// - /// Get an eventInfo for a given event name. Search type ancestors depth first if necessary. - /// Searches also in non public events. - /// - /// Name of the event, with the set_ or get_ prefix already removed - private EventInfo GetNonPublicEventFromName(string eventName) - { - var depthFirstProgress = new Queue(ctx.Mock.ImplementedInterfaces.Skip(1)); - depthFirstProgress.Enqueue(ctx.TargetType); - while (depthFirstProgress.Count > 0) - { - var currentType = depthFirstProgress.Dequeue(); - var eventInfo = currentType.GetEvent(eventName, BindingFlags.Instance | BindingFlags.NonPublic); - if (eventInfo != null) - { - return eventInfo; - } - - foreach (var implementedType in GetAncestorTypes(currentType)) - { - depthFirstProgress.Enqueue(implementedType); - } - } - - return null; - } - - - /// - /// Given a type return all of its ancestors, both types and interfaces. - /// - /// The type to find immediate ancestors of - private static IEnumerable GetAncestorTypes(Type initialType) - { - var baseType = initialType.BaseType; - if (baseType != null) - { - return new[] { baseType }; - } - - return initialType.GetInterfaces(); - } - InterceptorContext ctx; - public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorContext ctx, CurrentInterceptContext localctx) - { - this.ctx = ctx; - if (!FluentMockContext.IsActive) - { - //Special case for events - if (invocation.Method.IsEventAttach()) - { - var delegateInstance = (Delegate)invocation.Arguments[0]; - // TODO: validate we can get the event? - var eventInfo = this.GetEventFromName(invocation.Method.Name.Substring(4)); - - if (ctx.Mock.CallBase && !eventInfo.DeclaringType.IsInterface) - { - invocation.InvokeBase(); - } - else if (delegateInstance != null) - { - ctx.AddEventHandler(eventInfo, (Delegate)invocation.Arguments[0]); - } - - return InterceptionAction.Stop; - } - else if (invocation.Method.IsEventDetach()) - { - var delegateInstance = (Delegate)invocation.Arguments[0]; - // TODO: validate we can get the event? - var eventInfo = this.GetEventFromName(invocation.Method.Name.Substring(7)); - - if (ctx.Mock.CallBase && !eventInfo.DeclaringType.IsInterface) - { - invocation.InvokeBase(); - } - else if (delegateInstance != null) - { - ctx.RemoveEventHandler(eventInfo, (Delegate)invocation.Arguments[0]); - } - - return InterceptionAction.Stop; - } - - // Save to support Verify[expression] pattern. - // In a fluent invocation context, which is a recorder-like - // mode we use to evaluate delegates by actually running them, - // we don't want to count the invocation, or actually run - // previous setups. - ctx.AddInvocation(invocation); - } - return InterceptionAction.Continue; - } - } + invocation.Method.ReturnType != typeof(void)) + { + Mock recursiveMock; + if (ctx.Mock.InnerMocks.TryGetValue(invocation.Method, out recursiveMock)) + { + invocation.ReturnValue = recursiveMock.Object; + } + else + { + invocation.ReturnValue = ctx.Mock.DefaultValueProvider.ProvideDefault(invocation.Method); + } + return InterceptionAction.Stop; + } + return InterceptionAction.Continue; + } + } + + internal class InvokeBase : IInterceptStrategy + { + public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorContext ctx, CurrentInterceptContext localctx) + { + if (invocation.Method.DeclaringType == typeof(object) || // interface proxy + ctx.Mock.ImplementedInterfaces.Contains(invocation.Method.DeclaringType) && !invocation.Method.IsEventAttach() && !invocation.Method.IsEventDetach() && ctx.Mock.CallBase && !ctx.Mock.MockedType.IsInterface || // class proxy with explicitly implemented interfaces. The method's declaring type is the interface and the method couldn't be abstract + invocation.Method.DeclaringType.IsClass && !invocation.Method.IsAbstract && ctx.Mock.CallBase // class proxy + ) + { + // Invoke underlying implementation. + + // For mocked classes, if the target method was not abstract, + // invoke directly. + // Will only get here for Loose behavior. + // TODO: we may want to provide a way to skip this by the user. + invocation.InvokeBase(); + return InterceptionAction.Stop; + } + else + { + return InterceptionAction.Continue; + } + } + } + + internal class ExecuteCall : IInterceptStrategy + { + InterceptorContext ctx; + public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorContext ctx, CurrentInterceptContext localctx) + { + this.ctx = ctx; + IProxyCall currentCall = localctx.Call; + + if (currentCall != null) + { + currentCall.SetOutParameters(invocation); + + // We first execute, as there may be a Throws + // and therefore we might never get to the + // next line. + currentCall.Execute(invocation); + ThrowIfReturnValueRequired(currentCall, invocation); + return InterceptionAction.Stop; + } + else + { + return InterceptionAction.Continue; + } + } + private void ThrowIfReturnValueRequired(IProxyCall call, ICallContext invocation) + { + if (ctx.Behavior != MockBehavior.Loose && + invocation.Method != null && + invocation.Method.ReturnType != null && + invocation.Method.ReturnType != typeof(void)) + { + var methodCall = call as MethodCallReturn; + if (methodCall == null || !methodCall.HasReturnValue) + { + throw new MockException( + MockException.ExceptionReason.ReturnValueRequired, + ctx.Behavior, + invocation); + } + } + } + } + + internal class ExtractProxyCall : IInterceptStrategy + { + + public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorContext ctx, CurrentInterceptContext localctx) + { + localctx.Call = FluentMockContext.IsActive ? (IProxyCall)null : ctx.OrderedCalls.LastOrDefault(c => c.Matches(invocation)); + if (localctx.Call != null) + { + localctx.Call.EvaluatedSuccessfully(); + } + else if (!FluentMockContext.IsActive && ctx.Behavior == MockBehavior.Strict) + { + throw new MockException(MockException.ExceptionReason.NoSetup, ctx.Behavior, invocation); + } + + return InterceptionAction.Continue; + } + + } + + internal class InterceptMockPropertyMixin : IInterceptStrategy + { + public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorContext ctx, CurrentInterceptContext localctx) + { + var method = invocation.Method; + + if (typeof(IMocked).IsAssignableFrom(method.DeclaringType) && method.Name == "get_Mock") + { + invocation.ReturnValue = ctx.Mock; + return InterceptionAction.Stop; + } + + return InterceptionAction.Continue; + } + } + + internal class InterceptToStringMixin : IInterceptStrategy + { + public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorContext ctx, CurrentInterceptContext localctx) + { + var method = invocation.Method; + + // Only if there is no corresponding setup + if (IsObjectToStringMethod(method) && !ctx.OrderedCalls.Select(c => IsObjectToStringMethod(c.Method)).Any()) + { + invocation.ReturnValue = ctx.Mock.ToString() + ".Object"; + return InterceptionAction.Stop; + } + + return InterceptionAction.Continue; + } + + protected bool IsObjectToStringMethod(MethodInfo method) + { + if (method.DeclaringType == typeof(object) && method.Name == "ToString") + { + return true; + } + + return false; + } + } + + internal class HandleTracking : IInterceptStrategy + { + + public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorContext ctx, CurrentInterceptContext localctx) + { + // Track current invocation if we're in "record" mode in a fluent invocation context. + if (FluentMockContext.IsActive) + { + FluentMockContext.Current.Add(ctx.Mock, invocation); + } + return InterceptionAction.Continue; + } + } + + internal class HandleDestructor : IInterceptStrategy + { + public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorContext ctx, CurrentInterceptContext localctx) + { + return invocation.Method.IsDestructor() ? InterceptionAction.Stop : InterceptionAction.Continue; + } + } + + internal class AddActualInvocation : IInterceptStrategy + { + + /// + /// Get an eventInfo for a given event name. Search type ancestors depth first if necessary. + /// + /// Name of the event, with the set_ or get_ prefix already removed + private EventInfo GetEventFromName(string eventName) + { + var depthFirstProgress = new Queue(ctx.Mock.ImplementedInterfaces.Skip(1)); + depthFirstProgress.Enqueue(ctx.TargetType); + while (depthFirstProgress.Count > 0) + { + var currentType = depthFirstProgress.Dequeue(); + var eventInfo = currentType.GetEvent(eventName); + if (eventInfo != null) + { + return eventInfo; + } + + foreach (var implementedType in GetAncestorTypes(currentType)) + { + depthFirstProgress.Enqueue(implementedType); + } + } + return GetNonPublicEventFromName(eventName); + } + + /// + /// Get an eventInfo for a given event name. Search type ancestors depth first if necessary. + /// Searches also in non public events. + /// + /// Name of the event, with the set_ or get_ prefix already removed + private EventInfo GetNonPublicEventFromName(string eventName) + { + var depthFirstProgress = new Queue(ctx.Mock.ImplementedInterfaces.Skip(1)); + depthFirstProgress.Enqueue(ctx.TargetType); + while (depthFirstProgress.Count > 0) + { + var currentType = depthFirstProgress.Dequeue(); + var eventInfo = currentType.GetEvent(eventName, BindingFlags.Instance | BindingFlags.NonPublic); + if (eventInfo != null) + { + return eventInfo; + } + + foreach (var implementedType in GetAncestorTypes(currentType)) + { + depthFirstProgress.Enqueue(implementedType); + } + } + + return null; + } + + + /// + /// Given a type return all of its ancestors, both types and interfaces. + /// + /// The type to find immediate ancestors of + private static IEnumerable GetAncestorTypes(Type initialType) + { + var baseType = initialType.BaseType; + if (baseType != null) + { + return new[] { baseType }; + } + + return initialType.GetInterfaces(); + } + InterceptorContext ctx; + public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorContext ctx, CurrentInterceptContext localctx) + { + this.ctx = ctx; + if (!FluentMockContext.IsActive) + { + //Special case for events + if (invocation.Method.IsEventAttach()) + { + var delegateInstance = (Delegate)invocation.Arguments[0]; + // TODO: validate we can get the event? + var eventInfo = this.GetEventFromName(invocation.Method.Name.Substring(4)); + + if (ctx.Mock.CallBase && !eventInfo.DeclaringType.IsInterface) + { + invocation.InvokeBase(); + } + else if (delegateInstance != null) + { + ctx.AddEventHandler(eventInfo, (Delegate)invocation.Arguments[0]); + } + + return InterceptionAction.Stop; + } + else if (invocation.Method.IsEventDetach()) + { + var delegateInstance = (Delegate)invocation.Arguments[0]; + // TODO: validate we can get the event? + var eventInfo = this.GetEventFromName(invocation.Method.Name.Substring(7)); + + if (ctx.Mock.CallBase && !eventInfo.DeclaringType.IsInterface) + { + invocation.InvokeBase(); + } + else if (delegateInstance != null) + { + ctx.RemoveEventHandler(eventInfo, (Delegate)invocation.Arguments[0]); + } + + return InterceptionAction.Stop; + } + + // Save to support Verify[expression] pattern. + // In a fluent invocation context, which is a recorder-like + // mode we use to evaluate delegates by actually running them, + // we don't want to count the invocation, or actually run + // previous setups. + ctx.AddInvocation(invocation); + } + return InterceptionAction.Continue; + } + } } diff --git a/Source/Proxy/CastleProxyFactory.cs b/Source/Proxy/CastleProxyFactory.cs index f3db8e73b..490dcb557 100644 --- a/Source/Proxy/CastleProxyFactory.cs +++ b/Source/Proxy/CastleProxyFactory.cs @@ -78,13 +78,13 @@ static CastleProxyFactory() public object CreateProxy(Type mockType, ICallInterceptor interceptor, Type[] interfaces, object[] arguments) { if (mockType.IsInterface) - { - // Add type to additional interfaces and mock System.Object instead. - // This way it is also possible to mock System.Object methods. - Array.Resize(ref interfaces, interfaces.Length + 1); - interfaces[interfaces.Length - 1] = mockType; - mockType = typeof(object); - } + { + // Add type to additional interfaces and mock System.Object instead. + // This way it is also possible to mock System.Object methods. + Array.Resize(ref interfaces, interfaces.Length + 1); + interfaces[interfaces.Length - 1] = mockType; + mockType = typeof(object); + } try { @@ -112,23 +112,23 @@ public Type GetDelegateProxyInterface(Type delegateType, out MethodInfo delegate lock (this) { if (!delegateInterfaceCache.TryGetValue(delegateType, out delegateInterfaceType)) - { + { var interfaceName = String.Format(CultureInfo.InvariantCulture, "DelegateInterface_{0}_{1}", - delegateType.Name, delegateInterfaceSuffix++); + delegateType.Name, delegateInterfaceSuffix++); var moduleBuilder = generator.ProxyBuilder.ModuleScope.ObtainDynamicModule(true); var newTypeBuilder = moduleBuilder.DefineType(interfaceName, - TypeAttributes.Public | TypeAttributes.Interface | - TypeAttributes.Abstract); + TypeAttributes.Public | TypeAttributes.Interface | + TypeAttributes.Abstract); var invokeMethodOnDelegate = delegateType.GetMethod("Invoke"); var delegateParameterTypes = invokeMethodOnDelegate.GetParameters().Select(p => p.ParameterType).ToArray(); // Create a method on the interface with the same signature as the delegate. var newMethBuilder = newTypeBuilder.DefineMethod("Invoke", - MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Abstract, - CallingConventions.HasThis, - invokeMethodOnDelegate.ReturnType, delegateParameterTypes); + MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Abstract, + CallingConventions.HasThis, + invokeMethodOnDelegate.ReturnType, delegateParameterTypes); foreach (var param in invokeMethodOnDelegate.GetParameters()) { @@ -137,12 +137,12 @@ public Type GetDelegateProxyInterface(Type delegateType, out MethodInfo delegate delegateInterfaceType = newTypeBuilder.CreateType(); delegateInterfaceCache[delegateType] = delegateInterfaceType; - } - } + } + } delegateInterfaceMethod = delegateInterfaceType.GetMethod("Invoke"); return delegateInterfaceType; - } + } private static ProxyGenerator CreateProxyGenerator() { diff --git a/Source/Proxy/ProxyGenerationHelpers.cs b/Source/Proxy/ProxyGenerationHelpers.cs index 928eed404..fd8eb7621 100644 --- a/Source/Proxy/ProxyGenerationHelpers.cs +++ b/Source/Proxy/ProxyGenerationHelpers.cs @@ -7,31 +7,31 @@ namespace Moq.Proxy { - /// - /// Hook used to tells Castle which methods to proxy in mocked classes. - /// - /// Here we proxy the default methods Castle suggests (everything Object's methods) - /// plus Object.ToString(), so we can give mocks useful default names. - /// - /// This is required to allow Moq to mock ToString on proxy *class* implementations. - /// - internal class ProxyMethodHook : AllMethodsHook - { - protected static readonly Tuple[] GrantedMethods = new Tuple[] - { - Tuple.Create(typeof(object), "ToString"), - Tuple.Create(typeof(object), "Equals"), - Tuple.Create(typeof(object), "GetHashCode") - }; + /// + /// Hook used to tells Castle which methods to proxy in mocked classes. + /// + /// Here we proxy the default methods Castle suggests (everything Object's methods) + /// plus Object.ToString(), so we can give mocks useful default names. + /// + /// This is required to allow Moq to mock ToString on proxy *class* implementations. + /// + internal class ProxyMethodHook : AllMethodsHook + { + protected static readonly HashSet> GrantedMethods = new HashSet> + { + Tuple.Create(typeof(object), "ToString"), + Tuple.Create(typeof(object), "Equals"), + Tuple.Create(typeof(object), "GetHashCode") + }; - /// - /// Extends AllMethodsHook.ShouldInterceptMethod to also intercept Object.ToString(). - /// - public override bool ShouldInterceptMethod(Type type, MethodInfo methodInfo) + /// + /// Extends AllMethodsHook.ShouldInterceptMethod to also intercept Object.ToString(). + /// + public override bool ShouldInterceptMethod(Type type, MethodInfo methodInfo) { - bool isGranted = GrantedMethods.Contains(Tuple.Create(methodInfo.DeclaringType, methodInfo.Name)); - return base.ShouldInterceptMethod(type, methodInfo) || isGranted; - } + bool isGranted = GrantedMethods.Contains(Tuple.Create(methodInfo.DeclaringType, methodInfo.Name)); + return base.ShouldInterceptMethod(type, methodInfo) || isGranted; + } } ///// @@ -50,14 +50,14 @@ public override bool ShouldInterceptMethod(Type type, MethodInfo methodInfo) //[EditorBrowsable(EditorBrowsableState.Never)] //public abstract class InterfaceProxy //{ - // /// - // /// Overrides the default ToString implementation to instead find the mock for this mock.Object, - // /// and return MockName + '.Object' as the mocked object's ToString, to make it easy to relate - // /// mocks and mock object instances in error messages. - // /// - // public override string ToString() - // { - // return ((IMocked)this).Mock.ToString() + ".Object"; - // } - // } + // /// + // /// Overrides the default ToString implementation to instead find the mock for this mock.Object, + // /// and return MockName + '.Object' as the mocked object's ToString, to make it easy to relate + // /// mocks and mock object instances in error messages. + // /// + // public override string ToString() + // { + // return ((IMocked)this).Mock.ToString() + ".Object"; + // } + // } } diff --git a/UnitTests/MockFixture.cs b/UnitTests/MockFixture.cs index c0b571452..41aaacb38 100644 --- a/UnitTests/MockFixture.cs +++ b/UnitTests/MockFixture.cs @@ -72,7 +72,8 @@ public void PassesItsNameOnToTheResultingMockObjectWhenMockingClasses() public class ToStringOverrider { - public override string ToString() { + public override string ToString() + { return "real value"; } } @@ -254,65 +255,65 @@ public void OverridesBehaviorFromAbstractClass() Assert.False(mock.Object.Check("foo")); Assert.True(mock.Object.Check("bar")); - } - - [Fact] - public void CanSetupToString() - { - var mock = new Mock(); - mock.Setup(x => x.ToString()).Returns("This is me"); - - Assert.Equal("This is me", mock.Object.ToString()); - } - - [Fact] - public void CanSetupToStringForInterface() - { - var mock = new Mock(); - mock.Setup(x => x.ToString()).Returns("This is me"); - - Assert.Equal("This is me", mock.Object.ToString()); - } - - [Fact] - public void CanSetupGetHashCode() - { - var mock = new Mock(); - mock.Setup(x => x.GetHashCode()).Returns(527); - - Assert.Equal(527, mock.Object.GetHashCode()); - } - - [Fact] - public void CanSetupGetHashCodeForInterface() - { - var mock = new Mock(); - mock.Setup(x => x.GetHashCode()).Returns(527); - - Assert.Equal(527, mock.Object.GetHashCode()); - } - - [Fact] - public void CanSetupObjectEquals() - { - var mock = new Mock(); - mock.Setup(x => x.Equals(It.IsAny())).Returns((obj) => false); - var foo = mock.Object; - - Assert.True(!foo.Equals(foo)); - } - - [Fact] - public void CanSetupObjectEqualsForInterface() - { - var mock = new Mock(); - mock.Setup(x => x.Equals(It.IsAny())).Returns((obj) => false); - var foo = mock.Object; - - Assert.True(!foo.Equals(foo)); - } - - [Fact] + } + + [Fact] + public void CanSetupToString() + { + var mock = new Mock(); + mock.Setup(x => x.ToString()).Returns("This is me"); + + Assert.Equal("This is me", mock.Object.ToString()); + } + + [Fact] + public void CanSetupToStringForInterface() + { + var mock = new Mock(); + mock.Setup(x => x.ToString()).Returns("This is me"); + + Assert.Equal("This is me", mock.Object.ToString()); + } + + [Fact] + public void CanSetupGetHashCode() + { + var mock = new Mock(); + mock.Setup(x => x.GetHashCode()).Returns(527); + + Assert.Equal(527, mock.Object.GetHashCode()); + } + + [Fact] + public void CanSetupGetHashCodeForInterface() + { + var mock = new Mock(); + mock.Setup(x => x.GetHashCode()).Returns(527); + + Assert.Equal(527, mock.Object.GetHashCode()); + } + + [Fact] + public void CanSetupObjectEquals() + { + var mock = new Mock(); + mock.Setup(x => x.Equals(It.IsAny())).Returns((obj) => false); + var foo = mock.Object; + + Assert.True(!foo.Equals(foo)); + } + + [Fact] + public void CanSetupObjectEqualsForInterface() + { + var mock = new Mock(); + mock.Setup(x => x.Equals(It.IsAny())).Returns((obj) => false); + var foo = mock.Object; + + Assert.True(!foo.Equals(foo)); + } + + [Fact] public void CallsUnderlyingClassEquals() { var mock = new Mock(); @@ -390,14 +391,14 @@ public void ConstructsClassWithNoDefaultConstructorAndNullValue() [Fact] public void ThrowsIfNoMatchingConstructorFound() { - try - { - Console.WriteLine(new Mock(25, true).Object); - Assert.True(false, "Should have thrown an exception since constructor does not exist."); - } - catch (Exception) - { - } + try + { + Console.WriteLine(new Mock(25, true).Object); + Assert.True(false, "Should have thrown an exception since constructor does not exist."); + } + catch (Exception) + { + } } [Fact] From cb48123ca92ca1b2f87d61b489eac4f01bb487af Mon Sep 17 00:00:00 2001 From: Martin Hammerschmied Date: Wed, 10 Aug 2016 11:08:46 +0200 Subject: [PATCH 03/13] `System.Object` methods should always work, even in strict mode if none of them were set up. Fixes #273. --- Source/Interceptor.cs | 2 +- Source/InterceptorStrategies.cs | 27 +- UnitTests/Regressions/IssueReportsFixture.cs | 3332 +++++++++--------- 3 files changed, 1700 insertions(+), 1661 deletions(-) diff --git a/Source/Interceptor.cs b/Source/Interceptor.cs index 4503e5175..324e628df 100644 --- a/Source/Interceptor.cs +++ b/Source/Interceptor.cs @@ -118,7 +118,7 @@ private IEnumerable InterceptionStrategies() yield return new HandleDestructor(); yield return new HandleTracking(); yield return new InterceptMockPropertyMixin(); - yield return new InterceptToStringMixin(); + yield return new InterceptObjectMethodsMixin(); yield return new AddActualInvocation(); yield return new ExtractProxyCall(); yield return new ExecuteCall(); diff --git a/Source/InterceptorStrategies.cs b/Source/InterceptorStrategies.cs index e105b0fb3..6521b31cc 100644 --- a/Source/InterceptorStrategies.cs +++ b/Source/InterceptorStrategies.cs @@ -133,25 +133,42 @@ public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorCo } } - internal class InterceptToStringMixin : IInterceptStrategy + /// + /// Intercept strategy that handles `System.Object` methods. + /// + internal class InterceptObjectMethodsMixin : IInterceptStrategy { public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorContext ctx, CurrentInterceptContext localctx) { var method = invocation.Method; - // Only if there is no corresponding setup - if (IsObjectToStringMethod(method) && !ctx.OrderedCalls.Select(c => IsObjectToStringMethod(c.Method)).Any()) + // Only if there is no corresponding setup for `ToString()` + if (IsObjectMethod(method, "ToString") && !ctx.OrderedCalls.Select(c => IsObjectMethod(c.Method, "ToString")).Any()) { invocation.ReturnValue = ctx.Mock.ToString() + ".Object"; return InterceptionAction.Stop; } + // Only if there is no corresponding setup for `GetHashCode()` + if (IsObjectMethod(method, "GetHashCode") && !ctx.OrderedCalls.Select(c => IsObjectMethod(c.Method, "GetHashCode")).Any()) + { + invocation.ReturnValue = ctx.Mock.GetHashCode(); + return InterceptionAction.Stop; + } + + // Only if there is no corresponding setup for `Equals()` + if (IsObjectMethod(method, "Equals") && !ctx.OrderedCalls.Select(c => IsObjectMethod(c.Method, "Equals")).Any()) + { + invocation.ReturnValue = ReferenceEquals(invocation.Arguments.First(), ctx.Mock.Object); + return InterceptionAction.Stop; + } + return InterceptionAction.Continue; } - protected bool IsObjectToStringMethod(MethodInfo method) + protected bool IsObjectMethod(MethodInfo method, string name) { - if (method.DeclaringType == typeof(object) && method.Name == "ToString") + if (method.DeclaringType == typeof(object) && method.Name == name) { return true; } diff --git a/UnitTests/Regressions/IssueReportsFixture.cs b/UnitTests/Regressions/IssueReportsFixture.cs index 56565c472..05a2e2b99 100644 --- a/UnitTests/Regressions/IssueReportsFixture.cs +++ b/UnitTests/Regressions/IssueReportsFixture.cs @@ -22,60 +22,60 @@ // NOTE class without namespace public class _181 { - [Fact] - public void ReproTest() - { - var mock = new Mock(); - mock.Object.Dispose(); - - mock.Verify(d => d.Dispose()); - } + [Fact] + public void ReproTest() + { + var mock = new Mock(); + mock.Object.Dispose(); + + mock.Verify(d => d.Dispose()); + } } #endregion namespace Moq.Tests.Regressions { - public class IssueReportsFixture - { - // @ GitHub + public class IssueReportsFixture + { + // @ GitHub - #region #54 + #region #54 - public class Issue54 - { - public interface IFoo - { - void Bar(); - } + public class Issue54 + { + public interface IFoo + { + void Bar(); + } - [Fact] - public void when_as_disposable_then_succeeds() - { - var mock = new Mock(); - mock.Setup(x => x.Bar()); - mock.As().Setup(x => x.Dispose()); + [Fact] + public void when_as_disposable_then_succeeds() + { + var mock = new Mock(); + mock.Setup(x => x.Bar()); + mock.As().Setup(x => x.Dispose()); - Action testMock = (IFoo foo) => - { - foo.Bar(); + Action testMock = (IFoo foo) => + { + foo.Bar(); - var disposable = foo as IDisposable; + var disposable = foo as IDisposable; - if (disposable != null) - { - disposable.Dispose(); - } - }; + if (disposable != null) + { + disposable.Dispose(); + } + }; - testMock(mock.Object); + testMock(mock.Object); - mock.VerifyAll(); - } - } + mock.VerifyAll(); + } + } - #endregion + #endregion #region 47 & 62 #if !SILVERLIGHT @@ -228,7 +228,7 @@ public void when_a_method_doesnt_have_generic_parameters_exception_doesnt_includ #endregion // #176 - #region #184 + #region #184 public class Issue184 { @@ -247,1756 +247,1778 @@ public void strict_mock_accepts_null_as_nullable_guid_value() } } - #endregion // #184 + #endregion // #184 + + #region #252 + + public class Issue252 + { + [Fact] + public void SetupsWithSameArgumentsInDifferentOrderShouldNotOverwriteEachOther() + { + var mock = new Mock(); + + var a = new MyClass(); + var b = new MyClass(); - #region #252 + mock.Setup(m => m.Method(a, b)).Returns(1); + mock.Setup(m => m.Method(b, a)).Returns(2); - public class Issue252 - { - [Fact] - public void SetupsWithSameArgumentsInDifferentOrderShouldNotOverwriteEachOther() - { - var mock = new Mock(); + Assert.Equal(1, mock.Object.Method(a, b)); + Assert.Equal(2, mock.Object.Method(b, a)); + } + + public interface ISimpleInterface + { + int Method(MyClass a, MyClass b); + } + + public class MyClass { } + } + + #endregion // #252 - var a = new MyClass(); - var b = new MyClass(); - - mock.Setup(m => m.Method(a, b)).Returns(1); - mock.Setup(m => m.Method(b, a)).Returns(2); + #region #273 - Assert.Equal(1, mock.Object.Method(a, b)); - Assert.Equal(2, mock.Object.Method(b, a)); - } + public class Issue273 + { + [Fact] + public void SystemObjectMethodsShouldWorkInStrictMocks() + { + var mockObject = new Mock(MockBehavior.Strict).Object; - public interface ISimpleInterface - { - int Method(MyClass a, MyClass b); - } + Assert.IsType(typeof(int), mockObject.GetHashCode()); + Assert.IsType(typeof(string), mockObject.ToString()); + Assert.False(mockObject.Equals("ImNotTheObject")); + Assert.True(mockObject.Equals(mockObject)); + } - public class MyClass { } - } + public interface IMyInterface + { + } + } - #endregion // #252 + #endregion // #252 - // Old @ Google Code + // Old @ Google Code - #region #47 + #region #47 - [Fact] - public void ShouldReturnListFromDateTimeArg() - { - var items = new List() { "Foo", "Bar" }; + [Fact] + public void ShouldReturnListFromDateTimeArg() + { + var items = new List() { "Foo", "Bar" }; - var mock = new Mock(MockBehavior.Strict); - mock - .Setup(m => m.GetValuesSince(It.IsAny())) - .Returns(items); + var mock = new Mock(MockBehavior.Strict); + mock + .Setup(m => m.GetValuesSince(It.IsAny())) + .Returns(items); - var actual = mock.Object.GetValuesSince(DateTime.Now).ToList(); + var actual = mock.Object.GetValuesSince(DateTime.Now).ToList(); - Assert.Equal(items.Count, actual.Count); - } + Assert.Equal(items.Count, actual.Count); + } - public interface IMyClass - { - IEnumerable GetValuesSince(DateTime since); - } + public interface IMyClass + { + IEnumerable GetValuesSince(DateTime since); + } - #endregion + #endregion - #region #48 + #region #48 - public class Issue48 - { - [Fact] - public void ExpectsOnIndexer() - { - var mock = new Mock(); - mock.Setup(m => m[0]).Returns("a"); - mock.Setup(m => m[1]).Returns("b"); + public class Issue48 + { + [Fact] + public void ExpectsOnIndexer() + { + var mock = new Mock(); + mock.Setup(m => m[0]).Returns("a"); + mock.Setup(m => m[1]).Returns("b"); - Assert.Equal("a", mock.Object[0]); - Assert.Equal("b", mock.Object[1]); - Assert.Equal(default(string), mock.Object[2]); - } + Assert.Equal("a", mock.Object[0]); + Assert.Equal("b", mock.Object[1]); + Assert.Equal(default(string), mock.Object[2]); + } - public interface ISomeInterface - { - string this[int index] { get; set; } - } - } + public interface ISomeInterface + { + string this[int index] { get; set; } + } + } - #endregion + #endregion - #region #52 + #region #52 - [Fact] - public void ShouldNotOverridePreviousExpectation() - { - var ids = Enumerable.Range(1, 10); - var mock = new Mock(MockBehavior.Strict); + [Fact] + public void ShouldNotOverridePreviousExpectation() + { + var ids = Enumerable.Range(1, 10); + var mock = new Mock(MockBehavior.Strict); - foreach (var id in ids) - { - mock.Setup(x => x.DoSomething(id)); - } + foreach (var id in ids) + { + mock.Setup(x => x.DoSomething(id)); + } - var component = mock.Object; + var component = mock.Object; - foreach (var id in ids) - { - component.DoSomething(id); - } - } + foreach (var id in ids) + { + component.DoSomething(id); + } + } - public interface IOverwritingMethod - { - void DoSomething(int id); - } + public interface IOverwritingMethod + { + void DoSomething(int id); + } - #endregion + #endregion - #region #62 + #region #62 - public interface ISomething - { - void DoSomething() where U : T; - } + public interface ISomething + { + void DoSomething() where U : T; + } - [Fact] - public void CreatesMockWithGenericsConstraints() - { - var mock = new Mock>(); - } + [Fact] + public void CreatesMockWithGenericsConstraints() + { + var mock = new Mock>(); + } - #endregion + #endregion - #region #60 + #region #60 - public interface IFoo - { - void DoThings(object arg); - } + public interface IFoo + { + void DoThings(object arg); + } - [Fact] - public void TwoExpectations() - { - Mock mocked = new Mock(MockBehavior.Strict); - object arg1 = new object(); - object arg2 = new object(); + [Fact] + public void TwoExpectations() + { + Mock mocked = new Mock(MockBehavior.Strict); + object arg1 = new object(); + object arg2 = new object(); - mocked.Setup(m => m.DoThings(arg1)); - mocked.Setup(m => m.DoThings(arg2)); + mocked.Setup(m => m.DoThings(arg1)); + mocked.Setup(m => m.DoThings(arg2)); - mocked.Object.DoThings(arg1); - mocked.Object.DoThings(arg2); + mocked.Object.DoThings(arg1); + mocked.Object.DoThings(arg2); - mocked.VerifyAll(); - } + mocked.VerifyAll(); + } - #endregion + #endregion - #region #21 + #region #21 - [Fact] - public void MatchesLatestExpectations() - { - var mock = new Mock(); + [Fact] + public void MatchesLatestExpectations() + { + var mock = new Mock(); - mock.Setup(m => m.Method(It.IsAny())).Returns(0); - mock.Setup(m => m.Method(It.IsInRange(0, 20, Range.Inclusive))).Returns(1); + mock.Setup(m => m.Method(It.IsAny())).Returns(0); + mock.Setup(m => m.Method(It.IsInRange(0, 20, Range.Inclusive))).Returns(1); - mock.Setup(m => m.Method(5)).Returns(2); - mock.Setup(m => m.Method(10)).Returns(3); + mock.Setup(m => m.Method(5)).Returns(2); + mock.Setup(m => m.Method(10)).Returns(3); - Assert.Equal(3, mock.Object.Method(10)); - Assert.Equal(2, mock.Object.Method(5)); - Assert.Equal(1, mock.Object.Method(6)); - Assert.Equal(0, mock.Object.Method(25)); - } + Assert.Equal(3, mock.Object.Method(10)); + Assert.Equal(2, mock.Object.Method(5)); + Assert.Equal(1, mock.Object.Method(6)); + Assert.Equal(0, mock.Object.Method(25)); + } - public interface IEvaluateLatest - { - int Method(int value); - } + public interface IEvaluateLatest + { + int Method(int value); + } - #endregion + #endregion - #region #49 + #region #49 - [Fact] - public void UsesCustomMatchersWithGenerics() - { - var mock = new Mock(); + [Fact] + public void UsesCustomMatchersWithGenerics() + { + var mock = new Mock(); - mock.Setup(e => e.Method(IsEqual.To(5))).Returns(1); - mock.Setup(e => e.Method(IsEqual.To(6, "foo"))).Returns(2); + mock.Setup(e => e.Method(IsEqual.To(5))).Returns(1); + mock.Setup(e => e.Method(IsEqual.To(6, "foo"))).Returns(2); - Assert.Equal(1, mock.Object.Method(5)); - Assert.Equal(2, mock.Object.Method(6)); - } + Assert.Equal(1, mock.Object.Method(5)); + Assert.Equal(2, mock.Object.Method(6)); + } - public static class IsEqual - { + public static class IsEqual + { #pragma warning disable 618 - [Matcher] - public static T To(T value) - { - return value; - } + [Matcher] + public static T To(T value) + { + return value; + } #pragma warning restore 618 - public static bool To(T left, T right) - { - return left.Equals(right); - } + public static bool To(T left, T right) + { + return left.Equals(right); + } #pragma warning disable 618 - [Matcher] - public static T To(T value, U value2) - { - return value; - } + [Matcher] + public static T To(T value, U value2) + { + return value; + } #pragma warning restore 618 - public static bool To(T left, T right, U value) - { - return left.Equals(right); - } - } - - #endregion - - #region #68 - - [Fact] - public void GetMockCastedToObjectThrows() - { - var mock = new Mock(); - object m = mock.Object; - - Assert.Throws(() => Mock.Get(m)); - } - - #endregion - - #region #69 - - public interface IFooPtr - { - IntPtr Get(string input); - } - - [Fact] - public void ReturnsIntPtr() - { - Mock mock = new Mock(MockBehavior.Strict); - IntPtr ret = new IntPtr(3); - - mock.Setup(m => m.Get("a")).Returns(ret); - - IntPtr ret3 = mock.Object.Get("a"); - - Assert.Equal(ret, mock.Object.Get("a")); - } - - - #endregion - - #region #85 - - public class Issue85 - { - [Fact] - public void FooTest() - { - // Setup - var fooMock = new Mock(); - fooMock.CallBase = true; - fooMock.Setup(o => o.GetBar()).Returns(new Bar()); - var bar = ((IFoolery)fooMock.Object).DoStuffToBar(); - Assert.NotNull(bar); - } - - public interface IFoolery - { - Bar DoStuffToBar(); - } - - public class Foo : IFoolery - { - public virtual Bar GetBar() - { - return new Bar(); - } - - Bar IFoolery.DoStuffToBar() - { - return DoWeirdStuffToBar(); - } - - protected internal virtual Bar DoWeirdStuffToBar() - { - var bar = GetBar(); - //Would do stuff here. - return bar; - } - } - - public class Bar - { - } - } - - #endregion - - #region #89 - - public class Issue89 - { - [Fact] - public void That_last_expectation_should_win() - { - var mock = new Mock(); - mock.Setup(s => s.Get(1)).Returns("blah"); - mock.Setup(s => s.Get(It.IsAny())).Returns("foo"); - mock.Setup(s => s.Get(1)).Returns("bar"); - Assert.Equal("bar", mock.Object.Get(1)); - } - - public interface ISample - { - string Get(int i); - } - } - - #endregion - - #region #128 - - public class Issue128 - { - [Fact] - public void That_CallBase_on_interface_should_not_throw_exception() - { - var mock = new Mock() - { - DefaultValue = DefaultValue.Mock, - CallBase = true - }; - - var service = mock.Object.GetDataService(); - - var data = service.GetData(); - var result = data.Sum(); - - Assert.Equal( 0, result ); - } - - public interface IDataServiceFactory - { - IDataService GetDataService(); - } - - public interface IDataService - { - IList GetData(); - } - } - - #endregion - - #region #134 - - public class Issue134 - { - [Fact] - public void Test() - { - var target = new Mock(); - target.Setup(t => t.Submit(It.IsAny(), It.IsAny(), It.IsAny())); - - var e = Assert.Throws(() => target.VerifyAll()); - - Assert.Contains( - "IFoo t => t.Submit(It.IsAny(), It.IsAny(), new[] { It.IsAny() })", - e.Message); - } - - public interface IFoo - { - void Submit(string mailServer, string from, params int[] toRecipient); - } - } - - #endregion - - #region #136 - - public class _136 - { - // Fixed on PropertiesFixture.cs - } - - #endregion - - #region #138 - - public class _138 - { - public interface SuperFoo - { - string Bar { get; set; } - } - public interface Foo : SuperFoo - { - string Baz { get; set; } - } - - [Fact] - public void superFooMockSetupAllProperties() - { - var repo = new MockRepository(MockBehavior.Default); - var superFooMock = repo.Create(); - superFooMock.SetupAllProperties(); + public static bool To(T left, T right, U value) + { + return left.Equals(right); + } + } + + #endregion + + #region #68 - var superFoo = superFooMock.Object; - superFoo.Bar = "Bar"; - Assert.Equal("Bar", superFoo.Bar); - } - } + [Fact] + public void GetMockCastedToObjectThrows() + { + var mock = new Mock(); + object m = mock.Object; - #endregion + Assert.Throws(() => Mock.Get(m)); + } - #region #145 + #endregion - public class _145 - { - public interface IResolver - { - string Resolve(); - } + #region #69 - public class DataWriter - { - } + public interface IFooPtr + { + IntPtr Get(string input); + } - public class DataA { } - public class DataB { } + [Fact] + public void ReturnsIntPtr() + { + Mock mock = new Mock(MockBehavior.Strict); + IntPtr ret = new IntPtr(3); - [Fact] - public void ShouldDifferentiateBetweenGenericsParams() - { - var mock = new Mock(); - mock.Setup(m => m.Resolve>()).Returns("Success A"); + mock.Setup(m => m.Get("a")).Returns(ret); - Assert.Equal("Success A", mock.Object.Resolve>()); + IntPtr ret3 = mock.Object.Get("a"); - mock.Setup(m => m.Resolve>()).Returns("Success B"); + Assert.Equal(ret, mock.Object.Get("a")); + } - Assert.Equal("Success B", mock.Object.Resolve>()); - Assert.Equal("Success A", mock.Object.Resolve>()); - } - } + #endregion - #endregion + #region #85 - #region #111 & #155 + public class Issue85 + { + [Fact] + public void FooTest() + { + // Setup + var fooMock = new Mock(); + fooMock.CallBase = true; + fooMock.Setup(o => o.GetBar()).Returns(new Bar()); + var bar = ((IFoolery)fooMock.Object).DoStuffToBar(); + Assert.NotNull(bar); + } - public class _111 - { - [Fact] - public void TestTypedParamsWithNoParams() - { - var mock = new Mock(MockBehavior.Strict); - mock.Setup(p => p.Submit(It.IsAny(), It.IsAny())); + public interface IFoolery + { + Bar DoStuffToBar(); + } - mock.Object.Submit("foo"); + public class Foo : IFoolery + { + public virtual Bar GetBar() + { + return new Bar(); + } - mock.VerifyAll(); - } + Bar IFoolery.DoStuffToBar() + { + return DoWeirdStuffToBar(); + } - [Fact] - public void TestTypedParams() - { - var mock = new Mock(MockBehavior.Strict); - mock.Setup(p => p.Submit(It.IsAny(), It.IsAny())); + protected internal virtual Bar DoWeirdStuffToBar() + { + var bar = GetBar(); + //Would do stuff here. + return bar; + } + } - mock.Object.Submit("foo", 0, 1, 2); - - mock.VerifyAll(); - } - - [Fact] - public void TestObjectParamsWithoutParams() - { - var mock = new Mock(MockBehavior.Strict); - mock.Setup(p => p.Execute(It.IsAny(), It.IsAny())); - - mock.Object.Execute(1); - - mock.VerifyAll(); - } - - [Fact] - public void TestObjectParams() - { - var mock = new Mock(MockBehavior.Strict); - mock.Setup(p => p.Execute(It.IsAny(), It.IsAny())); - - mock.Object.Execute(1, "0", "1", "2"); - - mock.VerifyAll(); - } - - [Fact] - public void TestObjectParamsWithExpectedValues() - { - var mock = new Mock(MockBehavior.Strict); - mock.Setup(p => p.Execute(5, "foo", "bar")); - - Assert.Throws(() => mock.Object.Execute(5, "bar", "foo")); - - mock.Object.Execute(5, "foo", "bar"); - - mock.Verify(p => p.Execute(5, "foo", "bar")); - } - - [Fact] - public void TestObjectParamsWithArray() - { - var mock = new Mock(); - mock.Setup(p => p.Execute(It.IsAny(), It.IsAny(), It.IsAny())); - - mock.Object.Execute(1, new string[] { "0", "1" }, 3); - - mock.Verify(p => p.Execute(It.IsAny(), It.IsAny())); - mock.Verify(p => p.Execute(It.IsAny(), It.IsAny(), It.IsAny())); - mock.Verify(p => p.Execute(It.IsAny(), It.IsAny(), It.IsAny())); - } - - [Fact] - public void TestTypedParamsInEachArgument() - { - var mock = new Mock(MockBehavior.Strict); - mock.Setup(p => p.Submit(It.IsAny(), It.IsAny(), It.IsAny())); - - mock.Object.Submit("foo", 0, 1); - - mock.Verify(p => p.Submit(It.IsAny(), It.IsAny(), It.IsAny())); - mock.Verify(p => p.Submit(It.IsAny(), It.Is(a => a.Length == 2))); - mock.VerifyAll(); - } - - [Fact] - public void TestParamsWithReturnValue() - { - var mock = new Mock(); - mock.Setup(x => x.GetValue("Matt")).Returns("SomeString"); - - var ret = mock.Object.GetValue("Matt"); - Assert.Equal("SomeString", ret); - } - - public interface IParams - { - void Submit(string name, params int[] values); - void Execute(int value, params object[] values); - string GetValue(string name, params object[] args); - } - } - - #endregion - - #region #159 - - public class _159 - { - [Fact] - public void ImplicitIntToLong() - { - int id = 1; - var mock = new Mock(); - mock.Object.SetIt(id); - mock.Verify(x => x.SetIt(id)); - } - - [Fact] - public void ImplicitInterface() - { - var barMock = new Mock(); - var baz = new Baz(barMock.Object); - baz.DoBarFoo(new Foo()); - barMock.Verify(x => x.DoFoo(It.IsAny())); - } - - public interface IFoo - { - long Id { get; set; } - void SetIt(long it); - } - - public class Foo : IFoo - { - public long Id { get; set; } - public void SetIt(long it) { } - } - - public interface IBar - { - void DoFoo(IFoo foo); - } - - public class Baz - { - private readonly IBar _bar; - public Baz(IBar bar) - { - _bar = bar; - } - - public void DoBarFoo(IFoo foo) - { - _bar.DoFoo(foo); - } - } - } - - #endregion - - #region #152 - - public class _152 - { - public enum MembershipCreateStatus { Created, Duplicated, Invalid } - public interface IMembershipService - { - int MinPasswordLength { get; } - bool ValidateUser(string userName, string password); - MembershipCreateStatus CreateUser(string userName, string password, string email); - bool ChangePassword(string userName, string oldPassword, string newPassword); - } - - [Fact] - public void ShouldReturnEnum() - { - var provider = new Mock(); - - // For some reason, this particular lambda doesn't let me specify - // a method return value for the method even though it returns a - // MembershipCreateStatus enum - provider.Setup(p => p.CreateUser(string.Empty, string.Empty, string.Empty)).Returns(MembershipCreateStatus.Invalid); - - Assert.Equal(MembershipCreateStatus.Invalid, provider.Object.CreateUser("", "", "")); - } - } - - #endregion - - #region #153 - - public class _153 - { - public struct SomeClass // Struct just to avoid having to implement Equals/GetHashCode - { - public static implicit operator SomeClass(T t) - { - return new SomeClass(); - } - - public static SomeClass From(T t) - { - return t; - } - } - - public interface IIfc - { - int Get(SomeClass id); - } - - public class ImplicitConversionProblem - { - [Fact] - public void ImplicitSetupVerifyAll_Fails() - { - const string s = "XYZ"; - var mock = new Mock(); - mock.Setup(ifc => ifc.Get(s)).Returns(17); - - var result = mock.Object.Get(s); - - mock.VerifyAll(); // MockVerificationException here - Assert.Equal(17, result); - } - - [Fact] - public void ExplicitSetupVerifyAll_Works() - { - const string s = "XYZ"; - var mock = new Mock(); - mock.Setup(ifc => ifc.Get(SomeClass.From(s))).Returns(17); - - var result = mock.Object.Get(s); - - mock.VerifyAll(); - Assert.Equal(17, result); - } - - [Fact] - public void ExplicitSetupImplicitVerification_Fails() - { - const string s = "XYZ"; - var mock = new Mock(); - mock.Setup(ifc => ifc.Get(SomeClass.From(s))).Returns(17); - - var result = mock.Object.Get(s); - - // Here the problem can be seen even in the exception message: - // Invocation was not performed on the mock: ifc => ifc.Get("XYZ") - // -----------------------------------------------------------^ - mock.Verify(ifc => ifc.Get(s)); - Assert.Equal(17, result); - } - - [Fact] - public void ImplicitSetupExplicitVerification_Fails() - { - const string s = "XYZ"; - var mock = new Mock(); - mock.Setup(ifc => ifc.Get(s)).Returns(17); - - var result = mock.Object.Get(s); - - // This verification passes oddly enough - mock.Verify(ifc => ifc.Get(SomeClass.From(s))); - - // This assert fails, indicating that the setup was not used - Assert.Equal(17, result); - } - } - } - - #endregion - - #region #146 - - public class _146 - { - public interface IFoo - { - bool Property { get; set; } - string StringProperty { get; set; } - } - - [Fact] - public void StrictMockPropertySet() - { - var mock = new Mock(MockBehavior.Strict); - - mock.SetupSet(v => v.Property = false); - - Assert.Throws(() => mock.VerifySet(v => v.Property = false)); - - mock.Object.Property = false; - - mock.VerifySet(v => v.Property = false); - } - } - - #endregion - - #region #158 - - public class _158 - { - public class Foo - { - public virtual void Boo() - { - Bar(); - Bar(); - } - - protected virtual void Bar() - { - } - } + public class Bar + { + } + } -#pragma warning disable 618 - [Fact(Skip = "This setup doesn't make sense, and xUnit does not provide this message checking capability.")] - public void ShouldRenderCustomMessage() - { - var foo = new Mock { CallBase = true }; - foo.Protected().Setup("Bar").AtMostOnce().Verifiable("Hello"); - foo.Object.Boo(); - //Assert.Throws("Hello", () => foo.Object.Boo()); - } -#pragma warning restore 618 - } + #endregion - #endregion + #region #89 - #region #160 + public class Issue89 + { + [Fact] + public void That_last_expectation_should_win() + { + var mock = new Mock(); + mock.Setup(s => s.Get(1)).Returns("blah"); + mock.Setup(s => s.Get(It.IsAny())).Returns("foo"); + mock.Setup(s => s.Get(1)).Returns("bar"); + Assert.Equal("bar", mock.Object.Get(1)); + } -#if !SILVERLIGHT - public class _160 - { - [Fact] - public void ShouldMockHtmlControl() - { - // CallBase was missing - var htmlInputTextMock = new Mock() { CallBase = true }; - Assert.True(htmlInputTextMock.Object.Visible); - } - } -#endif + public interface ISample + { + string Get(int i); + } + } + + #endregion + + #region #128 + + public class Issue128 + { + [Fact] + public void That_CallBase_on_interface_should_not_throw_exception() + { + var mock = new Mock() + { + DefaultValue = DefaultValue.Mock, + CallBase = true + }; + + var service = mock.Object.GetDataService(); + + var data = service.GetData(); + var result = data.Sum(); + + Assert.Equal(0, result); + } - #endregion + public interface IDataServiceFactory + { + IDataService GetDataService(); + } - #region #161 + public interface IDataService + { + IList GetData(); + } + } - public class _161 - { - [Fact] - public void InvertEqualObjects() - { - var foo1 = new Foo { Id = "1" }; - var foo = new Foo { Id = "2" }; + #endregion - var dependency = new Mock(); + #region #134 - dependency.Setup(x => x.DoThis(foo, foo1)) - .Returns(new Foo()); - - var f = dependency.Object.DoThis(foo, foo1); - - dependency.Verify(x => x.DoThis(foo, foo1)); - dependency.Verify(x => x.DoThis(foo1, foo), Times.Never()); - } + public class Issue134 + { + [Fact] + public void Test() + { + var target = new Mock(); + target.Setup(t => t.Submit(It.IsAny(), It.IsAny(), It.IsAny())); - [Fact(Skip = "Wrong Equals implemention in the report. Won't Fix")] - public void ExampleFailingTest() - { - var foo1 = new Foo(); - var foo = new Foo(); - - var sut = new Perfectly_fine_yet_failing_test(); - var dependency = new Mock(); - - dependency.Setup(x => x.DoThis(foo, foo1)) - .Returns(new Foo()); - - sut.Do(dependency.Object, foo, foo1); + var e = Assert.Throws(() => target.VerifyAll()); - dependency.Verify(x => x.DoThis(foo, foo1)); - dependency.Verify(x => x.DoThis(foo1, foo), Times.Never()); - } + Assert.Contains( + "IFoo t => t.Submit(It.IsAny(), It.IsAny(), new[] { It.IsAny() })", + e.Message); + } - public class Perfectly_fine_yet_failing_test - { - public void Do(IDependency dependency, Foo foo, Foo foo1) - { - var foo2 = dependency.DoThis(foo, foo1); - if (foo2 == null) - foo2 = dependency.DoThis(foo1, foo); - } - } - - public interface IDependency - { - Foo DoThis(Foo foo, Foo foo1); - } + public interface IFoo + { + void Submit(string mailServer, string from, params int[] toRecipient); + } + } - public class Foo - { - public string Id { get; set; } + #endregion - public override bool Equals(object obj) - { - return obj is Foo && ((Foo)obj).Id == Id; - } - - public override int GetHashCode() - { - return base.GetHashCode(); - } - } - } + #region #136 - #endregion + public class _136 + { + // Fixed on PropertiesFixture.cs + } - #region #174 - - public class _174 - { - [Fact] - public void Test() - { - var serviceNo1Mock = new Mock(); - var collaboratorMock = new Mock(); - - collaboratorMock.Object.Collaborate(serviceNo1Mock.Object); + #endregion - collaboratorMock.Verify(o => o.Collaborate(serviceNo1Mock.Object)); - } - - public interface ISomeCollaborator - { - void Collaborate(IServiceNo1 serviceNo1); - } - - public interface IServiceNo1 : IEnumerable - { - } - } - - #endregion - - #region #177 - - public class _177 - { - [Fact] - public void Test() - { - var mock = new Mock(); - Assert.NotNull(mock.Object); - } - - public interface IMyInterface - { - void DoStuff() where TTo : TFrom; - } - } - - #endregion - - #region #184 - - public class _184 - { - [Fact] - public void Test() - { - var fooRaised = false; - var barRaised = false; - - var fooMock = new Mock(); - var barMock = fooMock.As(); - - fooMock.Object.FooEvent += (s, e) => fooRaised = true; - barMock.Object.BarEvent += (s, e) => barRaised = true; - - fooMock.Raise(m => m.FooEvent += null, EventArgs.Empty); - barMock.Raise(m => m.BarEvent += null, EventArgs.Empty); - - Assert.True(fooRaised); - Assert.True(barRaised); - } - - public interface IFoo - { - event EventHandler FooEvent; - } - - public interface IBar - { - event EventHandler BarEvent; - } - } - - #endregion - - #region #185 - - public class _185 - { - [Fact] - public void Test() - { - var mock = new Mock>(); - Assert.Throws(() => mock.Setup(l => l.FirstOrDefault()).Returns("Hello world")); - } - } - - #endregion - - #region #187 - - public class _187 - { - [Fact] - public void Test() - { - var mock = new Mock(); - - mock.Setup(r => r.Get()).Returns(new Object()); - mock.Setup(r => r.Get()).Returns(new Object()); - - Assert.NotNull(mock.Object.Get()); - Assert.NotNull(mock.Object.Get()); - } - - public class Foo - { - public class Inner - { - } - } - - public class Bar - { - public class Inner - { - } - } - - public interface IGeneric - { - object Get() where T : new(); - } - } - - #endregion - - #region #183 - - public class _183 - { - [Fact] - public void Test() - { - var mock = new Mock(); - mock.Setup(m => m.Execute(1)); - mock.Setup(m => m.Execute(It.IsInRange(2, 20, Range.Exclusive))); - mock.Setup(m => m.Execute(3, "Caption")); - - mock.Object.Execute(3); - mock.Object.Execute(4); - mock.Object.Execute(5); - - var e = Assert.Throws(() => mock.Verify(m => m.Execute(0))); - Assert.Contains( - "\r\nConfigured setups:" + - "\r\nm => m.Execute(1), Times.Never" + - "\r\nm => m.Execute(It.IsInRange(2, 20, Range.Exclusive)), Times.Exactly(3)", - e.Message); - } - - [Fact] - public void TestGeneric() - { - var mock = new Mock(); - mock.Setup(m => m.Execute(1, 10)); - mock.Setup(m => m.Execute(1, "Foo")); - - mock.Object.Execute(1, 10); - - var e = Assert.Throws(() => mock.Verify(m => m.Execute(1, 1))); - Assert.Contains( - "\r\nConfigured setups:\r\nm => m.Execute(1, 10), Times.Once", - e.Message); - } - - [Fact] - public void TestNoSetups() - { - var mock = new Mock(); - - var e = Assert.Throws(() => mock.Verify(m => m.Execute(1))); - Assert.Contains("\r\nNo setups configured.", e.Message); - - } - - public interface IFoo - { - void Execute(int param); - void Execute(int param, string caption); - void Execute(int p, T param); - } - } - - #endregion - - #region #186 - - public class _186 - { - [Fact] - public void TestVerifyMessage() - { - var mock = new Mock(); - mock.Setup(m => m.OnExecute()); - - var e = Assert.Throws(() => mock.Verify(m => m.Execute())); - Assert.True(e.Message.StartsWith("Invalid verify")); - } - - public class Foo - { - public void Execute() - { - this.OnExecute(); - } - - public virtual void OnExecute() - { - throw new NotImplementedException(); - } - } - } - - #endregion - - #region #190 - - public class _190 - { - [Fact] - public void Test() - { - var mock = new Mock().As(); - mock.SetupAllProperties(); - - ISite site = new FooSite(); - mock.Object.Site = site; - Assert.Same(site, mock.Object.Site); - } - - public class FooSite : ISite - { - public IComponent Component - { - get { throw new NotImplementedException(); } - } - - public IContainer Container - { - get { throw new NotImplementedException(); } - } - - public bool DesignMode - { - get { throw new NotImplementedException(); } - } - - public string Name - { - get { throw new NotImplementedException(); } - set { throw new NotImplementedException(); } - } - - public object GetService(Type serviceType) - { - throw new NotImplementedException(); - } - } - - } - - #endregion - - #region #204 - - public class _204 - { - [Fact] - public void Test() - { - var mock = new Mock(); - mock.Setup(x => x.Select(u => u.Id == 100)) - .Returns(new User() { Id = 100 }); - - var user = mock.Object.Select(usr => usr.Id == 100); - Assert.Equal(100, user.Id); - mock.Verify(x => x.Select(usr => usr.Id == 100), Times.Once()); - - user = mock.Object.Select(usr => usr.Id == 101); - Assert.Null(user); - mock.Verify(x => x.Select(usr => usr.Id == 101), Times.Once()); - - mock.Verify(x => x.Select(usr => usr.Id == 102), Times.Never()); - mock.Verify(x => x.Select(It.IsAny>>()), Times.Exactly(2)); - } - - public interface IRepository - { - T Select(Expression> filter) where T : class; - } - - public class User - { - public int Id { get; set; } - } - } - - #endregion - - #region #205 - - public class _205 - { - [Fact] - public void Test() - { - new Mock().SetupAllProperties(); - } - - public interface IFoo - { - string Error { get; set; } - string this[int index] { get; set; } - } - } - - #endregion - - #region #223 - - public class _223 - { - [Fact] - public void TestSetup() - { - this.TestSetupHelper(); - } - - public void TestSetupHelper() where T : class, IFoo - { - var expected = 2; - - var target = new Mock(); - target.Setup(p => p.DoInt32(0)).Returns(expected); - target.Setup(p => p.DoGeneric(0)).Returns(expected); - - Assert.Equal(expected, target.Object.DoInt32(0)); - Assert.Equal(expected, target.Object.DoGeneric(0)); - } - - public interface IFoo - { - int DoInt32(int value); - T DoGeneric(int value); - } - - public class Foo : IFoo - { - public virtual int DoInt32(int value) - { - return 4; - } - - public virtual int DoGeneric(int value) - { - return 5; - } - } - } - - #endregion - - #region #229 - - public class _229 - { - [Fact] - public void Test() - { - var target = new Mock { CallBase = true }; - - var raised = false; - target.Object.MyEvent += (s, e) => raised = true; - target.Object.RaiseMyEvent(); - - Assert.True(raised); - } - - public class Foo - { - public virtual event EventHandler MyEvent; - - public void RaiseMyEvent() - { - if (this.MyEvent != null) - { - this.MyEvent(this, EventArgs.Empty); - } - } - } - } - - #endregion - - #region #230 - - public class _230 - { - [Fact] - public void ByteArrayCallbackArgumentShouldNotBeNull() - { - var data = new byte[] { 2, 1, 2 }; - var stream = new Mock(); - - stream.SetupGet(m => m.Length) - .Returns(data.Length); - stream.Setup(m => m.Read(It.IsAny(), It.IsAny(), It.IsAny())) - .Callback((b, o, c) => data.CopyTo(b, 0)) - .Returns(data.Length); - - var contents = new byte[stream.Object.Length]; - stream.Object.Read(contents, 0, (int)stream.Object.Length); - } - } - - #endregion - - #region #232 - - public class _232 - { - [Fact] - public void Test() - { - var repository = new Mock(); - var svc = new Service(repository.Object); - - svc.Create(); - - repository.Verify(r => r.Insert(It.IsAny()), Times.Once()); - repository.Verify(r => r.Insert(It.IsAny()), Times.Once()); - repository.Verify(r => r.Insert(It.IsAny()), Times.Exactly(2)); - } - - public interface IRepository - { - void Insert(IEntity entity); - } - - public interface IEntity - { - } - - public class Foo : IEntity - { - } - - public class Bar : IEntity - { - } - - public class Service - { - private IRepository repository; - - public Service(IRepository repository) - { - this.repository = repository; - } - - public void Create() - { - repository.Insert(new Foo()); - repository.Insert(new Bar()); - } - } - } - - #endregion - - #region #242 - - public class _242 - { - [Fact] - public void PropertyChangedTest() - { - var mock = new Mock(); - int callbacks = 0; - mock.Object.PropertyChanged += (sender, args) => callbacks++; - - mock.Raise(m => m.PropertyChanged += null, new PropertyChangedEventArgs("Foo")); - Assert.Equal(1, callbacks); - } - - public class PropertyChangedBase : INotifyPropertyChanged - { - public virtual event PropertyChangedEventHandler PropertyChanged = (s, e) => { }; - } - - public class PropertyChangedInherited : PropertyChangedBase - { - } - } - - #endregion - - #region #245 - - public class _245 - { - [Fact] - public void Test() - { - var mock = new Mock(); - - ITest instance; - instance = mock.Object; - } - - public interface ITest - { - void Do() where T2 : T1; - } - } - - #endregion - - #region #251 - - public class _251 - { - [Fact] - public void Test() - { - var repositoryMock = new Mock>(); - - var repository = repositoryMock.Object; - repository.Save("test"); - - repositoryMock.Verify(m => m.Save("test")); - } - - public interface IRepository - { - void Save(string value); - } - - public interface IRepository : IRepository - { - void Save(T value); - } - } - - #endregion - - #region #256 - - public class _256 - { - [Fact] - public void TestFinalizeNotMocked() - { - var mock = new Mock(MockBehavior.Strict); - mock.Setup(m => m.Foo).Returns(10); - mock.Setup(m => m.Bar).Returns("Hello mocked world!"); - var instance = mock.Object; - - Assert.Equal(10, instance.Foo); - } - - public class ClassWithFinalizer - { - public virtual int Foo { get; set; } - public virtual string Bar { get; set; } - - ~ClassWithFinalizer() - { - - } - } - } - - #endregion - - #region #261 - - public class _261 - { - [Fact] - public void Test() - { - var mock = new Mock(); - mock.Protected().SetupSet("Status", 42); + #region #138 - mock.Object.SetStatus(42); + public class _138 + { + public interface SuperFoo + { + string Bar { get; set; } + } + public interface Foo : SuperFoo + { + string Baz { get; set; } + } - mock.Protected().VerifySet("Status", Times.Once(), 42); - } + [Fact] + public void superFooMockSetupAllProperties() + { + var repo = new MockRepository(MockBehavior.Default); + var superFooMock = repo.Create(); + superFooMock.SetupAllProperties(); - public class Foo - { - public virtual int Status { get; protected set; } + var superFoo = superFooMock.Object; + superFoo.Bar = "Bar"; + Assert.Equal("Bar", superFoo.Bar); + } + } - internal void SetStatus(int value) - { - this.Status = value; - } - } - } - - #endregion + #endregion - #region #267 + #region #145 - public class _267 - { - public interface IPerformOperation - { - string Operation(object input); - } + public class _145 + { + public interface IResolver + { + string Resolve(); + } - public class OperationUser - { - private readonly IPerformOperation m_OperationPerformer; + public class DataWriter + { + } - public OperationUser(IPerformOperation operationPerformer) - { - m_OperationPerformer = operationPerformer; - } - - public string DoOperation(object input) - { - return m_OperationPerformer.Operation(input); - } - } + public class DataA { } + public class DataB { } - public class HelperSetup - { - private Mock m_OperationStub; + [Fact] + public void ShouldDifferentiateBetweenGenericsParams() + { + var mock = new Mock(); + mock.Setup(m => m.Resolve>()).Returns("Success A"); - public HelperSetup() - { - m_OperationStub = new Mock(); - } + Assert.Equal("Success A", mock.Object.Resolve>()); - [Fact] - public void InlineSetupTest() - { - m_OperationStub.Setup(m => m.Operation(It.IsAny())).Returns(value => "test"); - m_OperationStub.Setup(m => m.Operation(It.IsAny())).Returns(value => "25"); + mock.Setup(m => m.Resolve>()).Returns("Success B"); - var operationUser = new OperationUser(m_OperationStub.Object); + Assert.Equal("Success B", mock.Object.Resolve>()); + Assert.Equal("Success A", mock.Object.Resolve>()); + } - var intOperationResult = operationUser.DoOperation(9); - var stringOperationResult = operationUser.DoOperation("Hello"); + } - Assert.Equal("25", intOperationResult); - Assert.Equal("test", stringOperationResult); - } + #endregion - [Fact] - public void HelperSetupTest() - { - SetupOperationStub(value => "test"); - SetupOperationStub(value => "25"); + #region #111 & #155 - var operationUser = new OperationUser(m_OperationStub.Object); + public class _111 + { + [Fact] + public void TestTypedParamsWithNoParams() + { + var mock = new Mock(MockBehavior.Strict); + mock.Setup(p => p.Submit(It.IsAny(), It.IsAny())); - var intOperationResult = operationUser.DoOperation(9); - var stringOperationResult = operationUser.DoOperation("Hello"); + mock.Object.Submit("foo"); - Assert.Equal("25", intOperationResult); - Assert.Equal("test", stringOperationResult); - } + mock.VerifyAll(); + } - private void SetupOperationStub(Func valueFunction) - { - m_OperationStub.Setup(m => m.Operation(It.IsAny())).Returns(valueFunction); - } - } - } + [Fact] + public void TestTypedParams() + { + var mock = new Mock(MockBehavior.Strict); + mock.Setup(p => p.Submit(It.IsAny(), It.IsAny())); - #endregion + mock.Object.Submit("foo", 0, 1, 2); - #region #273 + mock.VerifyAll(); + } -#if !SILVERLIGHT + [Fact] + public void TestObjectParamsWithoutParams() + { + var mock = new Mock(MockBehavior.Strict); + mock.Setup(p => p.Execute(It.IsAny(), It.IsAny())); - public class _273 - { - [Fact] - public void WhenMockingAnExternalInterface_ThenItWorks() - { - Assert.NotNull(new Mock().Object); - Assert.NotNull(Mock.Of()); - Assert.NotNull(new Mock().Object); - Assert.NotNull(new Mock().Object); - Assert.NotNull(Mock.Of()); - Assert.NotNull(new Mock().Object); - Assert.NotNull(new Mock().Object); - } - - public class Baz : ClassLibrary2.Bar - { - } - } + mock.Object.Execute(1); -#endif + mock.VerifyAll(); + } + + [Fact] + public void TestObjectParams() + { + var mock = new Mock(MockBehavior.Strict); + mock.Setup(p => p.Execute(It.IsAny(), It.IsAny())); - #endregion + mock.Object.Execute(1, "0", "1", "2"); - #region #325 + mock.VerifyAll(); + } + + [Fact] + public void TestObjectParamsWithExpectedValues() + { + var mock = new Mock(MockBehavior.Strict); + mock.Setup(p => p.Execute(5, "foo", "bar")); - public class _325 - { - [Fact] - public void SubscribingWorks() - { - var target = new Mock { CallBase = true }; - target.As(); + Assert.Throws(() => mock.Object.Execute(5, "bar", "foo")); - var bar = (IBar)target.Object; - var raised = false; - bar.SomeEvent += (sender, e) => raised = true; + mock.Object.Execute(5, "foo", "bar"); - target.As().Raise(b => b.SomeEvent += null, EventArgs.Empty); + mock.Verify(p => p.Execute(5, "foo", "bar")); + } - Assert.True(raised); - } + [Fact] + public void TestObjectParamsWithArray() + { + var mock = new Mock(); + mock.Setup(p => p.Execute(It.IsAny(), It.IsAny(), It.IsAny())); - [Fact] - public void UnsubscribingWorks() - { - var target = new Mock { CallBase = true }; - target.As(); + mock.Object.Execute(1, new string[] { "0", "1" }, 3); - var bar = (IBar)target.Object; - var raised = false; - EventHandler handler = (sender, e) => raised = true; - bar.SomeEvent += handler; - bar.SomeEvent -= handler; + mock.Verify(p => p.Execute(It.IsAny(), It.IsAny())); + mock.Verify(p => p.Execute(It.IsAny(), It.IsAny(), It.IsAny())); + mock.Verify(p => p.Execute(It.IsAny(), It.IsAny(), It.IsAny())); + } - target.As().Raise(b => b.SomeEvent += null, EventArgs.Empty); + [Fact] + public void TestTypedParamsInEachArgument() + { + var mock = new Mock(MockBehavior.Strict); + mock.Setup(p => p.Submit(It.IsAny(), It.IsAny(), It.IsAny())); - Assert.False(raised); - } + mock.Object.Submit("foo", 0, 1); - public class Foo - { - } + mock.Verify(p => p.Submit(It.IsAny(), It.IsAny(), It.IsAny())); + mock.Verify(p => p.Submit(It.IsAny(), It.Is(a => a.Length == 2))); + mock.VerifyAll(); + } - public interface IBar - { - event EventHandler SomeEvent; - } + [Fact] + public void TestParamsWithReturnValue() + { + var mock = new Mock(); + mock.Setup(x => x.GetValue("Matt")).Returns("SomeString"); - } + var ret = mock.Object.GetValue("Matt"); + Assert.Equal("SomeString", ret); + } - #endregion + public interface IParams + { + void Submit(string name, params int[] values); + void Execute(int value, params object[] values); + string GetValue(string name, params object[] args); + } + } - #region #326 + #endregion -#if !SILVERLIGHT + #region #159 - public class _326 - { - [Fact] - public void ShouldSupportMockingWinFormsControl() - { - var foo = new Mock(); - var bar = foo.Object; - } - } + public class _159 + { + [Fact] + public void ImplicitIntToLong() + { + int id = 1; + var mock = new Mock(); + mock.Object.SetIt(id); + mock.Verify(x => x.SetIt(id)); + } -#endif + [Fact] + public void ImplicitInterface() + { + var barMock = new Mock(); + var baz = new Baz(barMock.Object); + baz.DoBarFoo(new Foo()); + barMock.Verify(x => x.DoFoo(It.IsAny())); + } + + public interface IFoo + { + long Id { get; set; } + void SetIt(long it); + } + + public class Foo : IFoo + { + public long Id { get; set; } + public void SetIt(long it) { } + } + + public interface IBar + { + void DoFoo(IFoo foo); + } + + public class Baz + { + private readonly IBar _bar; + public Baz(IBar bar) + { + _bar = bar; + } + + public void DoBarFoo(IFoo foo) + { + _bar.DoFoo(foo); + } + } + } + + #endregion + + #region #152 + + public class _152 + { + public enum MembershipCreateStatus { Created, Duplicated, Invalid } + public interface IMembershipService + { + int MinPasswordLength { get; } + bool ValidateUser(string userName, string password); + MembershipCreateStatus CreateUser(string userName, string password, string email); + bool ChangePassword(string userName, string oldPassword, string newPassword); + } + + [Fact] + public void ShouldReturnEnum() + { + var provider = new Mock(); + + // For some reason, this particular lambda doesn't let me specify + // a method return value for the method even though it returns a + // MembershipCreateStatus enum + provider.Setup(p => p.CreateUser(string.Empty, string.Empty, string.Empty)).Returns(MembershipCreateStatus.Invalid); - #endregion - - #region Recursive issue - - public class RecursiveFixture - { - [Fact] - public void TestRecursive() - { - var mock = new Mock() { DefaultValue = DefaultValue.Mock }; - mock.Setup(c => c.HttpContext.Response.Write("stuff")); - - mock.Object.HttpContext.Response.Write("stuff"); - mock.Object.HttpContext.Response.ShouldEncode = true; - - Assert.Throws(() => mock.VerifySet( - c => c.HttpContext.Response.ShouldEncode = It.IsAny(), - Times.Never())); - } - - public class ControllerContext - { - public virtual HttpContext HttpContext { get; set; } - } - - public abstract class HttpContext - { - protected HttpContext() - { - } - - public virtual HttpResponse Response - { - get { throw new NotImplementedException(); } - } - } - - public abstract class HttpResponse - { - protected HttpResponse() - { - } - - public virtual bool ShouldEncode - { - get { throw new NotImplementedException(); } - set { throw new NotImplementedException(); } - } - - public virtual void Write(string s) - { - throw new NotImplementedException(); - } - } - } - - #endregion - - #region #250 - - /// - /// Silverlight MethodInfo protected constructor is internal, unlike desktop .NET - /// - public class _250 - { - [Fact] - public void Test() - { - var target = new Mock(); - - Assert.NotNull(target.Object); - } - } - - #endregion - - #region Matcher should work with Convert - - public class MatcherConvertFixture - { - public interface IFoo - { - string M(long l); - } - - [Fact] - public void MatcherDoesNotIgnoreConvert() - { - var mock = new Mock(MockBehavior.Strict); - mock.Setup(x => x.M(int.Parse("2"))).Returns("OK"); - Assert.Equal("OK", mock.Object.M(2L)); - } - } - - #endregion - } + Assert.Equal(MembershipCreateStatus.Invalid, provider.Object.CreateUser("", "", "")); + } + } + + #endregion + + #region #153 + + public class _153 + { + public struct SomeClass // Struct just to avoid having to implement Equals/GetHashCode + { + public static implicit operator SomeClass(T t) + { + return new SomeClass(); + } + + public static SomeClass From(T t) + { + return t; + } + } + + public interface IIfc + { + int Get(SomeClass id); + } + + public class ImplicitConversionProblem + { + [Fact] + public void ImplicitSetupVerifyAll_Fails() + { + const string s = "XYZ"; + var mock = new Mock(); + mock.Setup(ifc => ifc.Get(s)).Returns(17); + + var result = mock.Object.Get(s); + + mock.VerifyAll(); // MockVerificationException here + Assert.Equal(17, result); + } + + [Fact] + public void ExplicitSetupVerifyAll_Works() + { + const string s = "XYZ"; + var mock = new Mock(); + mock.Setup(ifc => ifc.Get(SomeClass.From(s))).Returns(17); + + var result = mock.Object.Get(s); + + mock.VerifyAll(); + Assert.Equal(17, result); + } + + [Fact] + public void ExplicitSetupImplicitVerification_Fails() + { + const string s = "XYZ"; + var mock = new Mock(); + mock.Setup(ifc => ifc.Get(SomeClass.From(s))).Returns(17); + + var result = mock.Object.Get(s); + + // Here the problem can be seen even in the exception message: + // Invocation was not performed on the mock: ifc => ifc.Get("XYZ") + // -----------------------------------------------------------^ + mock.Verify(ifc => ifc.Get(s)); + Assert.Equal(17, result); + } + + [Fact] + public void ImplicitSetupExplicitVerification_Fails() + { + const string s = "XYZ"; + var mock = new Mock(); + mock.Setup(ifc => ifc.Get(s)).Returns(17); + + var result = mock.Object.Get(s); + + // This verification passes oddly enough + mock.Verify(ifc => ifc.Get(SomeClass.From(s))); + + // This assert fails, indicating that the setup was not used + Assert.Equal(17, result); + } + } + } + + #endregion + + #region #146 + + public class _146 + { + public interface IFoo + { + bool Property { get; set; } + string StringProperty { get; set; } + } + + [Fact] + public void StrictMockPropertySet() + { + var mock = new Mock(MockBehavior.Strict); + + mock.SetupSet(v => v.Property = false); + + Assert.Throws(() => mock.VerifySet(v => v.Property = false)); + + mock.Object.Property = false; + + mock.VerifySet(v => v.Property = false); + } + } + + #endregion + + #region #158 + + public class _158 + { + public class Foo + { + public virtual void Boo() + { + Bar(); + Bar(); + } + + protected virtual void Bar() + { + } + } + +#pragma warning disable 618 + [Fact(Skip = "This setup doesn't make sense, and xUnit does not provide this message checking capability.")] + public void ShouldRenderCustomMessage() + { + var foo = new Mock { CallBase = true }; + foo.Protected().Setup("Bar").AtMostOnce().Verifiable("Hello"); + foo.Object.Boo(); + //Assert.Throws("Hello", () => foo.Object.Boo()); + } +#pragma warning restore 618 + } + + #endregion + + #region #160 + +#if !SILVERLIGHT + public class _160 + { + [Fact] + public void ShouldMockHtmlControl() + { + // CallBase was missing + var htmlInputTextMock = new Mock() { CallBase = true }; + Assert.True(htmlInputTextMock.Object.Visible); + } + } +#endif + + #endregion + + #region #161 + + public class _161 + { + [Fact] + public void InvertEqualObjects() + { + var foo1 = new Foo { Id = "1" }; + var foo = new Foo { Id = "2" }; + + var dependency = new Mock(); + + dependency.Setup(x => x.DoThis(foo, foo1)) + .Returns(new Foo()); + + var f = dependency.Object.DoThis(foo, foo1); + + dependency.Verify(x => x.DoThis(foo, foo1)); + dependency.Verify(x => x.DoThis(foo1, foo), Times.Never()); + } + + [Fact(Skip = "Wrong Equals implemention in the report. Won't Fix")] + public void ExampleFailingTest() + { + var foo1 = new Foo(); + var foo = new Foo(); + + var sut = new Perfectly_fine_yet_failing_test(); + var dependency = new Mock(); + + dependency.Setup(x => x.DoThis(foo, foo1)) + .Returns(new Foo()); + + sut.Do(dependency.Object, foo, foo1); + + dependency.Verify(x => x.DoThis(foo, foo1)); + dependency.Verify(x => x.DoThis(foo1, foo), Times.Never()); + } + + public class Perfectly_fine_yet_failing_test + { + public void Do(IDependency dependency, Foo foo, Foo foo1) + { + var foo2 = dependency.DoThis(foo, foo1); + if (foo2 == null) + foo2 = dependency.DoThis(foo1, foo); + } + } + + public interface IDependency + { + Foo DoThis(Foo foo, Foo foo1); + } + + public class Foo + { + public string Id { get; set; } + + public override bool Equals(object obj) + { + return obj is Foo && ((Foo)obj).Id == Id; + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + } + } + + #endregion + + #region #174 + + public class _174 + { + [Fact] + public void Test() + { + var serviceNo1Mock = new Mock(); + var collaboratorMock = new Mock(); + + collaboratorMock.Object.Collaborate(serviceNo1Mock.Object); + + collaboratorMock.Verify(o => o.Collaborate(serviceNo1Mock.Object)); + } + + public interface ISomeCollaborator + { + void Collaborate(IServiceNo1 serviceNo1); + } + + public interface IServiceNo1 : IEnumerable + { + } + } + + #endregion + + #region #177 + + public class _177 + { + [Fact] + public void Test() + { + var mock = new Mock(); + Assert.NotNull(mock.Object); + } + + public interface IMyInterface + { + void DoStuff() where TTo : TFrom; + } + } + + #endregion + + #region #184 + + public class _184 + { + [Fact] + public void Test() + { + var fooRaised = false; + var barRaised = false; + + var fooMock = new Mock(); + var barMock = fooMock.As(); + + fooMock.Object.FooEvent += (s, e) => fooRaised = true; + barMock.Object.BarEvent += (s, e) => barRaised = true; + + fooMock.Raise(m => m.FooEvent += null, EventArgs.Empty); + barMock.Raise(m => m.BarEvent += null, EventArgs.Empty); + + Assert.True(fooRaised); + Assert.True(barRaised); + } + + public interface IFoo + { + event EventHandler FooEvent; + } + + public interface IBar + { + event EventHandler BarEvent; + } + } + + #endregion + + #region #185 + + public class _185 + { + [Fact] + public void Test() + { + var mock = new Mock>(); + Assert.Throws(() => mock.Setup(l => l.FirstOrDefault()).Returns("Hello world")); + } + } + + #endregion + + #region #187 + + public class _187 + { + [Fact] + public void Test() + { + var mock = new Mock(); + + mock.Setup(r => r.Get()).Returns(new Object()); + mock.Setup(r => r.Get()).Returns(new Object()); + + Assert.NotNull(mock.Object.Get()); + Assert.NotNull(mock.Object.Get()); + } + + public class Foo + { + public class Inner + { + } + } + + public class Bar + { + public class Inner + { + } + } + + public interface IGeneric + { + object Get() where T : new(); + } + } + + #endregion + + #region #183 + + public class _183 + { + [Fact] + public void Test() + { + var mock = new Mock(); + mock.Setup(m => m.Execute(1)); + mock.Setup(m => m.Execute(It.IsInRange(2, 20, Range.Exclusive))); + mock.Setup(m => m.Execute(3, "Caption")); + + mock.Object.Execute(3); + mock.Object.Execute(4); + mock.Object.Execute(5); + + var e = Assert.Throws(() => mock.Verify(m => m.Execute(0))); + Assert.Contains( + "\r\nConfigured setups:" + + "\r\nm => m.Execute(1), Times.Never" + + "\r\nm => m.Execute(It.IsInRange(2, 20, Range.Exclusive)), Times.Exactly(3)", + e.Message); + } + + [Fact] + public void TestGeneric() + { + var mock = new Mock(); + mock.Setup(m => m.Execute(1, 10)); + mock.Setup(m => m.Execute(1, "Foo")); + + mock.Object.Execute(1, 10); + + var e = Assert.Throws(() => mock.Verify(m => m.Execute(1, 1))); + Assert.Contains( + "\r\nConfigured setups:\r\nm => m.Execute(1, 10), Times.Once", + e.Message); + } + + [Fact] + public void TestNoSetups() + { + var mock = new Mock(); + + var e = Assert.Throws(() => mock.Verify(m => m.Execute(1))); + Assert.Contains("\r\nNo setups configured.", e.Message); + + } + + public interface IFoo + { + void Execute(int param); + void Execute(int param, string caption); + void Execute(int p, T param); + } + } + + #endregion + + #region #186 + + public class _186 + { + [Fact] + public void TestVerifyMessage() + { + var mock = new Mock(); + mock.Setup(m => m.OnExecute()); + + var e = Assert.Throws(() => mock.Verify(m => m.Execute())); + Assert.True(e.Message.StartsWith("Invalid verify")); + } + + public class Foo + { + public void Execute() + { + this.OnExecute(); + } + + public virtual void OnExecute() + { + throw new NotImplementedException(); + } + } + } + + #endregion + + #region #190 + + public class _190 + { + [Fact] + public void Test() + { + var mock = new Mock().As(); + mock.SetupAllProperties(); + + ISite site = new FooSite(); + mock.Object.Site = site; + Assert.Same(site, mock.Object.Site); + } + + public class FooSite : ISite + { + public IComponent Component + { + get { throw new NotImplementedException(); } + } + + public IContainer Container + { + get { throw new NotImplementedException(); } + } + + public bool DesignMode + { + get { throw new NotImplementedException(); } + } + + public string Name + { + get { throw new NotImplementedException(); } + set { throw new NotImplementedException(); } + } + + public object GetService(Type serviceType) + { + throw new NotImplementedException(); + } + } + + } + + #endregion + + #region #204 + + public class _204 + { + [Fact] + public void Test() + { + var mock = new Mock(); + mock.Setup(x => x.Select(u => u.Id == 100)) + .Returns(new User() { Id = 100 }); + + var user = mock.Object.Select(usr => usr.Id == 100); + Assert.Equal(100, user.Id); + mock.Verify(x => x.Select(usr => usr.Id == 100), Times.Once()); + + user = mock.Object.Select(usr => usr.Id == 101); + Assert.Null(user); + mock.Verify(x => x.Select(usr => usr.Id == 101), Times.Once()); + + mock.Verify(x => x.Select(usr => usr.Id == 102), Times.Never()); + mock.Verify(x => x.Select(It.IsAny>>()), Times.Exactly(2)); + } + + public interface IRepository + { + T Select(Expression> filter) where T : class; + } + + public class User + { + public int Id { get; set; } + } + } + + #endregion + + #region #205 + + public class _205 + { + [Fact] + public void Test() + { + new Mock().SetupAllProperties(); + } + + public interface IFoo + { + string Error { get; set; } + string this[int index] { get; set; } + } + } + + #endregion + + #region #223 + + public class _223 + { + [Fact] + public void TestSetup() + { + this.TestSetupHelper(); + } + + public void TestSetupHelper() where T : class, IFoo + { + var expected = 2; + + var target = new Mock(); + target.Setup(p => p.DoInt32(0)).Returns(expected); + target.Setup(p => p.DoGeneric(0)).Returns(expected); + + Assert.Equal(expected, target.Object.DoInt32(0)); + Assert.Equal(expected, target.Object.DoGeneric(0)); + } + + public interface IFoo + { + int DoInt32(int value); + T DoGeneric(int value); + } + + public class Foo : IFoo + { + public virtual int DoInt32(int value) + { + return 4; + } + + public virtual int DoGeneric(int value) + { + return 5; + } + } + } + + #endregion + + #region #229 + + public class _229 + { + [Fact] + public void Test() + { + var target = new Mock { CallBase = true }; + + var raised = false; + target.Object.MyEvent += (s, e) => raised = true; + target.Object.RaiseMyEvent(); + + Assert.True(raised); + } + + public class Foo + { + public virtual event EventHandler MyEvent; + + public void RaiseMyEvent() + { + if (this.MyEvent != null) + { + this.MyEvent(this, EventArgs.Empty); + } + } + } + } + + #endregion + + #region #230 + + public class _230 + { + [Fact] + public void ByteArrayCallbackArgumentShouldNotBeNull() + { + var data = new byte[] { 2, 1, 2 }; + var stream = new Mock(); + + stream.SetupGet(m => m.Length) + .Returns(data.Length); + stream.Setup(m => m.Read(It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((b, o, c) => data.CopyTo(b, 0)) + .Returns(data.Length); + + var contents = new byte[stream.Object.Length]; + stream.Object.Read(contents, 0, (int)stream.Object.Length); + } + } + + #endregion + + #region #232 + + public class _232 + { + [Fact] + public void Test() + { + var repository = new Mock(); + var svc = new Service(repository.Object); + + svc.Create(); + + repository.Verify(r => r.Insert(It.IsAny()), Times.Once()); + repository.Verify(r => r.Insert(It.IsAny()), Times.Once()); + repository.Verify(r => r.Insert(It.IsAny()), Times.Exactly(2)); + } + + public interface IRepository + { + void Insert(IEntity entity); + } + + public interface IEntity + { + } + + public class Foo : IEntity + { + } + + public class Bar : IEntity + { + } + + public class Service + { + private IRepository repository; + + public Service(IRepository repository) + { + this.repository = repository; + } + + public void Create() + { + repository.Insert(new Foo()); + repository.Insert(new Bar()); + } + } + } + + #endregion + + #region #242 + + public class _242 + { + [Fact] + public void PropertyChangedTest() + { + var mock = new Mock(); + int callbacks = 0; + mock.Object.PropertyChanged += (sender, args) => callbacks++; + + mock.Raise(m => m.PropertyChanged += null, new PropertyChangedEventArgs("Foo")); + Assert.Equal(1, callbacks); + } + + public class PropertyChangedBase : INotifyPropertyChanged + { + public virtual event PropertyChangedEventHandler PropertyChanged = (s, e) => { }; + } + + public class PropertyChangedInherited : PropertyChangedBase + { + } + } + + #endregion + + #region #245 + + public class _245 + { + [Fact] + public void Test() + { + var mock = new Mock(); + + ITest instance; + instance = mock.Object; + } + + public interface ITest + { + void Do() where T2 : T1; + } + } + + #endregion + + #region #251 + + public class _251 + { + [Fact] + public void Test() + { + var repositoryMock = new Mock>(); + + var repository = repositoryMock.Object; + repository.Save("test"); + + repositoryMock.Verify(m => m.Save("test")); + } + + public interface IRepository + { + void Save(string value); + } + + public interface IRepository : IRepository + { + void Save(T value); + } + } + + #endregion + + #region #256 + + public class _256 + { + [Fact] + public void TestFinalizeNotMocked() + { + var mock = new Mock(MockBehavior.Strict); + mock.Setup(m => m.Foo).Returns(10); + mock.Setup(m => m.Bar).Returns("Hello mocked world!"); + var instance = mock.Object; + + Assert.Equal(10, instance.Foo); + } + + public class ClassWithFinalizer + { + public virtual int Foo { get; set; } + public virtual string Bar { get; set; } + + ~ClassWithFinalizer() + { + + } + } + } + + #endregion + + #region #261 + + public class _261 + { + [Fact] + public void Test() + { + var mock = new Mock(); + mock.Protected().SetupSet("Status", 42); + + mock.Object.SetStatus(42); + + mock.Protected().VerifySet("Status", Times.Once(), 42); + } + + public class Foo + { + public virtual int Status { get; protected set; } + + internal void SetStatus(int value) + { + this.Status = value; + } + } + } + + #endregion + + #region #267 + + public class _267 + { + public interface IPerformOperation + { + string Operation(object input); + } + + public class OperationUser + { + private readonly IPerformOperation m_OperationPerformer; + + public OperationUser(IPerformOperation operationPerformer) + { + m_OperationPerformer = operationPerformer; + } + + public string DoOperation(object input) + { + return m_OperationPerformer.Operation(input); + } + } + + public class HelperSetup + { + private Mock m_OperationStub; + + public HelperSetup() + { + m_OperationStub = new Mock(); + } + + [Fact] + public void InlineSetupTest() + { + m_OperationStub.Setup(m => m.Operation(It.IsAny())).Returns(value => "test"); + m_OperationStub.Setup(m => m.Operation(It.IsAny())).Returns(value => "25"); + + var operationUser = new OperationUser(m_OperationStub.Object); + + var intOperationResult = operationUser.DoOperation(9); + var stringOperationResult = operationUser.DoOperation("Hello"); + + Assert.Equal("25", intOperationResult); + Assert.Equal("test", stringOperationResult); + } + + [Fact] + public void HelperSetupTest() + { + SetupOperationStub(value => "test"); + SetupOperationStub(value => "25"); + + var operationUser = new OperationUser(m_OperationStub.Object); + + var intOperationResult = operationUser.DoOperation(9); + var stringOperationResult = operationUser.DoOperation("Hello"); + + Assert.Equal("25", intOperationResult); + Assert.Equal("test", stringOperationResult); + } + + private void SetupOperationStub(Func valueFunction) + { + m_OperationStub.Setup(m => m.Operation(It.IsAny())).Returns(valueFunction); + } + } + } + + #endregion + + #region #273 + +#if !SILVERLIGHT + + public class _273 + { + [Fact] + public void WhenMockingAnExternalInterface_ThenItWorks() + { + Assert.NotNull(new Mock().Object); + Assert.NotNull(Mock.Of()); + Assert.NotNull(new Mock().Object); + Assert.NotNull(new Mock().Object); + Assert.NotNull(Mock.Of()); + Assert.NotNull(new Mock().Object); + Assert.NotNull(new Mock().Object); + } + + public class Baz : ClassLibrary2.Bar + { + } + } + +#endif + + #endregion + + #region #325 + + public class _325 + { + [Fact] + public void SubscribingWorks() + { + var target = new Mock { CallBase = true }; + target.As(); + + var bar = (IBar)target.Object; + var raised = false; + bar.SomeEvent += (sender, e) => raised = true; + + target.As().Raise(b => b.SomeEvent += null, EventArgs.Empty); + + Assert.True(raised); + } + + [Fact] + public void UnsubscribingWorks() + { + var target = new Mock { CallBase = true }; + target.As(); + + var bar = (IBar)target.Object; + var raised = false; + EventHandler handler = (sender, e) => raised = true; + bar.SomeEvent += handler; + bar.SomeEvent -= handler; + + target.As().Raise(b => b.SomeEvent += null, EventArgs.Empty); + + Assert.False(raised); + } + + public class Foo + { + } + + public interface IBar + { + event EventHandler SomeEvent; + } + + } + + #endregion + + #region #326 + +#if !SILVERLIGHT + + public class _326 + { + [Fact] + public void ShouldSupportMockingWinFormsControl() + { + var foo = new Mock(); + var bar = foo.Object; + } + } + +#endif + + #endregion + + #region Recursive issue + + public class RecursiveFixture + { + [Fact] + public void TestRecursive() + { + var mock = new Mock() { DefaultValue = DefaultValue.Mock }; + mock.Setup(c => c.HttpContext.Response.Write("stuff")); + + mock.Object.HttpContext.Response.Write("stuff"); + mock.Object.HttpContext.Response.ShouldEncode = true; + + Assert.Throws(() => mock.VerifySet( + c => c.HttpContext.Response.ShouldEncode = It.IsAny(), + Times.Never())); + } + + public class ControllerContext + { + public virtual HttpContext HttpContext { get; set; } + } + + public abstract class HttpContext + { + protected HttpContext() + { + } + + public virtual HttpResponse Response + { + get { throw new NotImplementedException(); } + } + } + + public abstract class HttpResponse + { + protected HttpResponse() + { + } + + public virtual bool ShouldEncode + { + get { throw new NotImplementedException(); } + set { throw new NotImplementedException(); } + } + + public virtual void Write(string s) + { + throw new NotImplementedException(); + } + } + } + + #endregion + + #region #250 + + /// + /// Silverlight MethodInfo protected constructor is internal, unlike desktop .NET + /// + public class _250 + { + [Fact] + public void Test() + { + var target = new Mock(); + + Assert.NotNull(target.Object); + } + } + + #endregion + + #region Matcher should work with Convert + + public class MatcherConvertFixture + { + public interface IFoo + { + string M(long l); + } + + [Fact] + public void MatcherDoesNotIgnoreConvert() + { + var mock = new Mock(MockBehavior.Strict); + mock.Setup(x => x.M(int.Parse("2"))).Returns("OK"); + Assert.Equal("OK", mock.Object.M(2L)); + } + } + + #endregion + } } From ca7e8db08851ff9b224ed4312db108ebe3e9a42d Mon Sep 17 00:00:00 2001 From: vladonemo Date: Wed, 10 Aug 2016 16:12:13 +0200 Subject: [PATCH 04/13] Fixing issues #161 and #163 (#245) --- Source/Extensions.cs | 53 ++- Source/Mock.cs | 486 ++++++++++++---------- Source/Moq.csproj | 1 + Source/SerializableTypesValueProvider.cs | 34 ++ UnitTests/Moq.Tests.csproj | 1 + UnitTests/Regressions/FluentMockIssues.cs | 73 ++++ 6 files changed, 403 insertions(+), 245 deletions(-) create mode 100644 Source/SerializableTypesValueProvider.cs create mode 100644 UnitTests/Regressions/FluentMockIssues.cs diff --git a/Source/Extensions.cs b/Source/Extensions.cs index 005cb1001..581ccd88d 100644 --- a/Source/Extensions.cs +++ b/Source/Extensions.cs @@ -44,21 +44,24 @@ using System.Globalization; using System.Linq; using System.Reflection; +using System.Runtime.Serialization; + using Moq.Proxy; using System.Linq.Expressions; using Moq.Properties; + namespace Moq { internal static class Extensions { - static readonly FieldInfo remoteStackTraceString = typeof(Exception).GetField("_remoteStackTraceString", - BindingFlags.Instance | BindingFlags.NonPublic); + private static readonly FieldInfo remoteStackTraceString = typeof (Exception).GetField("_remoteStackTraceString", + BindingFlags.Instance | BindingFlags.NonPublic); public static TAttribute GetCustomAttribute(this ICustomAttributeProvider source, bool inherit) where TAttribute : Attribute { - object[] attrs = source.GetCustomAttributes(typeof(TAttribute), inherit); + object[] attrs = source.GetCustomAttributes(typeof (TAttribute), inherit); if (attrs.Length == 0) { @@ -66,7 +69,7 @@ public static TAttribute GetCustomAttribute(this ICustomAttributePro } else { - return (TAttribute)attrs[0]; + return (TAttribute) attrs[0]; } } @@ -80,15 +83,15 @@ public static string Format(this ICallContext invocation) if (invocation.Method.IsPropertySetter()) { return invocation.Method.DeclaringType.Name + "." + - invocation.Method.Name.Substring(4) + " = " + GetValue(invocation.Arguments.First()); + invocation.Method.Name.Substring(4) + " = " + GetValue(invocation.Arguments.First()); } - + var genericParameters = invocation.Method.IsGenericMethod - ? "<" + string.Join(", ", invocation.Method.GetGenericArguments().Select(t => t.Name).ToArray()) + ">" - : ""; + ? "<" + string.Join(", ", invocation.Method.GetGenericArguments().Select(t => t.Name).ToArray()) + ">" + : ""; return invocation.Method.DeclaringType.Name + "." + invocation.Method.Name + genericParameters + "(" + - string.Join(", ", invocation.Arguments.Select(a => GetValue(a)).ToArray()) + ")"; + string.Join(", ", invocation.Arguments.Select(a => GetValue(a)).ToArray()) + ")"; } public static string GetValue(object value) @@ -103,10 +106,10 @@ public static string GetValue(object value) { return "\"" + typedValue + "\""; } - if (value is IEnumerable) - { - return "[" + string.Join(", ", ((IEnumerable) value).OfType().Select(GetValue)) + "]"; - } + if (value is IEnumerable) + { + return "[" + string.Join(", ", ((IEnumerable) value).OfType().Select(GetValue)) + "]"; + } return value.ToString(); } @@ -119,7 +122,7 @@ public static object InvokePreserveStack(this Delegate del, params object[] args catch (TargetInvocationException ex) { #if SILVERLIGHT - /* The test listed below fails when we call the setValue in silverlight... + /* The test listed below fails when we call the setValue in silverlight... * * * Assembly: @@ -150,7 +153,7 @@ public static void SetStackTrace(this Exception exception, string stackTrace) /// public static bool IsDelegate(this Type t) { - return t.IsSubclassOf(typeof(Delegate)); + return t.IsSubclassOf(typeof (Delegate)); } public static void ThrowIfNotMockeable(this Type typeToMock) @@ -193,6 +196,26 @@ public static bool IsMockeable(this Type typeToMock) return typeToMock.IsInterface || typeToMock.IsAbstract || typeToMock.IsDelegate() || (typeToMock.IsClass && !typeToMock.IsSealed); } + public static bool IsSerializableMockable(this Type typeToMock) + { + return typeToMock.ContainsDeserializationConstructor() && typeToMock.IsGetObjectDataVirtual(); + } + + private static bool IsGetObjectDataVirtual(this Type typeToMock) + { + var getObjectDataMethod = typeToMock.GetInterfaceMap(typeof (ISerializable)).TargetMethods[0]; + return !getObjectDataMethod.IsPrivate && getObjectDataMethod.IsVirtual && !getObjectDataMethod.IsFinal; + } + + private static bool ContainsDeserializationConstructor(this Type typeToMock) + { + return typeToMock.GetConstructor( + BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, + null, + new[] {typeof (SerializationInfo), typeof (StreamingContext)}, + null) != null; + } + public static bool CanOverride(this MethodBase method) { return method.IsVirtual && !method.IsFinal && !method.IsPrivate; diff --git a/Source/Mock.cs b/Source/Mock.cs index 2e7cc5687..3ed5ca1a2 100644 --- a/Source/Mock.cs +++ b/Source/Mock.cs @@ -1,4 +1,4 @@ -//Copyright (c) 2007. Clarius Consulting, Manas Technology Solutions, InSTEDD +//Copyright (c) 2007. Clarius Consulting, Manas Technology Solutions, InSTEDD //http://code.google.com/p/moq/ //All rights reserved. @@ -50,13 +50,13 @@ using System.Reflection; using System.Runtime.CompilerServices; + namespace Moq { /// public abstract partial class Mock : IFluentInterface { private bool isInitialized; - private bool callBase; private DefaultValue defaultValue = DefaultValue.Empty; private IDefaultValueProvider defaultValueProvider = new EmptyDefaultValueProvider(); @@ -94,7 +94,7 @@ public static Mock Get(T mocked) where T : class var imockedType = mocked.GetType().GetInterface("IMocked`1", false); var mockedType = imockedType.GetGenericArguments()[0]; - if (mock.ImplementedInterfaces.Contains(typeof(T))) + if (mock.ImplementedInterfaces.Contains(typeof (T))) { return mock.As(); } @@ -107,8 +107,8 @@ public static Mock Get(T mocked) where T : class // the generic parameters. var types = string.Join( ", ", - new[] { mockedType } - // Skip first interface which is always our internal IMocked + new[] {mockedType} + // Skip first interface which is always our internal IMocked .Concat(mock.ImplementedInterfaces.Skip(1)) .Select(t => t.Name) .ToArray()); @@ -116,40 +116,36 @@ public static Mock Get(T mocked) where T : class throw new ArgumentException(string.Format( CultureInfo.CurrentCulture, Resources.InvalidMockGetType, - typeof(T).Name, + typeof (T).Name, types)); } throw new ArgumentException(Resources.ObjectInstanceNotMock, "mocked"); } - + /// public static void Verify(params Mock[] mocks) - { - foreach (var mock in mocks) - { - mock.Verify(); - } - } - + { + foreach (var mock in mocks) + { + mock.Verify(); + } + } + /// public static void VerifyAll(params Mock[] mocks) - { - foreach (var mock in mocks) - { - mock.VerifyAll(); - } - } + { + foreach (var mock in mocks) + { + mock.VerifyAll(); + } + } /// public virtual MockBehavior Behavior { get; internal set; } /// - public virtual bool CallBase - { - get { return this.callBase; } - set { this.callBase = value; } - } + public virtual bool CallBase { get; set; } /// public virtual DefaultValue DefaultValue @@ -161,14 +157,16 @@ public virtual DefaultValue DefaultValue private void SetDefaultValue(DefaultValue value) { this.defaultValue = value; - this.defaultValueProvider = defaultValue == DefaultValue.Mock ? - new MockDefaultValueProvider(this) : - new EmptyDefaultValueProvider(); + this.defaultValueProvider = defaultValue == DefaultValue.Mock + ? new MockDefaultValueProvider(this) + : new EmptyDefaultValueProvider(); } /// - [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Object", Justification = "Exposes the mocked object instance, so it's appropriate.")] - [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods", Justification = "The public Object property is the only one visible to Moq consumers. The protected member is for internal use only.")] + [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Object", + Justification = "Exposes the mocked object instance, so it's appropriate.")] + [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods", + Justification = "The public Object property is the only one visible to Moq consumers. The protected member is for internal use only.")] public object Object { get { return this.GetObject(); } @@ -186,7 +184,8 @@ private object GetObject() internal virtual ConcurrentDictionary InnerMocks { get; private set; } /// - [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This is actually the protected virtual implementation of the property Object.")] + [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", + Justification = "This is actually the protected virtual implementation of the property Object.")] protected abstract object OnGetObject(); /// @@ -202,23 +201,23 @@ private object GetObject() /// internal abstract MethodInfo DelegateInterfaceMethod { get; } - /// - /// Allows to check whether expression conversion to the - /// must be performed on the mock, without causing unnecessarily early initialization of - /// the mock instance, which breaks As{T}. - /// - internal abstract bool IsDelegateMock { get; } - - /// - /// Specifies the class that will determine the default - /// value to return when invocations are made that - /// have no setups and need to return a default - /// value (for loose mocks). - /// - internal IDefaultValueProvider DefaultValueProvider - { - get { return this.defaultValueProvider; } - } + /// + /// Allows to check whether expression conversion to the + /// must be performed on the mock, without causing unnecessarily early initialization of + /// the mock instance, which breaks As{T}. + /// + internal abstract bool IsDelegateMock { get; } + + /// + /// Specifies the class that will determine the default + /// value to return when invocations are made that + /// have no setups and need to return a default + /// value (for loose mocks). + /// + internal IDefaultValueProvider DefaultValueProvider + { + get { return this.defaultValueProvider; } + } /// /// Exposes the list of extra interfaces implemented by the mock. @@ -285,7 +284,7 @@ internal static void Verify( ThrowIfVerifyNonVirtual(expression, method); var args = methodCall.Arguments.ToArray(); - var expected = new MethodCall(mock, null, expression, method, args) { FailMessage = failMessage }; + var expected = new MethodCall(mock, null, expression, method, args) {FailMessage = failMessage}; VerifyCalls(GetInterceptor(methodCall.Object, mock), expected, expression, times); } @@ -331,7 +330,7 @@ internal static void VerifyGet( { FailMessage = failMessage }; - VerifyCalls(GetInterceptor(((MemberExpression)expression.Body).Expression, mock), expected, expression, times); + VerifyCalls(GetInterceptor(((MemberExpression) expression.Body).Expression, mock), expected, expression, times); } internal static void VerifySet( @@ -344,11 +343,11 @@ internal static void VerifySet( Interceptor targetInterceptor = null; Expression expression = null; var expected = SetupSetImpl>(mock, setterExpression, (m, expr, method, value) => - { - targetInterceptor = m.Interceptor; - expression = expr; - return new MethodCall(m, null, expr, method, value) { FailMessage = failMessage }; - }); + { + targetInterceptor = m.Interceptor; + expression = expr; + return new MethodCall(m, null, expr, method, value) {FailMessage = failMessage}; + }); VerifyCalls(targetInterceptor, expected, expression, times); } @@ -358,7 +357,7 @@ private static bool AreSameMethod(Expression left, Expression right) var leftLambda = left.ToLambda(); var rightLambda = right.ToLambda(); if (leftLambda != null && rightLambda != null && - leftLambda.Body is MethodCallExpression && rightLambda.Body is MethodCallExpression) + leftLambda.Body is MethodCallExpression && rightLambda.Body is MethodCallExpression) { return leftLambda.ToMethodCall().Method == rightLambda.ToMethodCall().Method; } @@ -372,14 +371,14 @@ private static void VerifyCalls( Expression expression, Times times) { - // .Where does an enumeration, and calls to a mocked method concurrent to VerifyCalls might change the content of ActualCalls. therefore, it is necessary to take a snapshot, using ToList(), so that concurrent calls will not impact the ongoing verification. - var actualCalls = targetInterceptor.InterceptionContext.ActualInvocations.ToList(); + // .Where does an enumeration, and calls to a mocked method concurrent to VerifyCalls might change the content of ActualCalls. therefore, it is necessary to take a snapshot, using ToList(), so that concurrent calls will not impact the ongoing verification. + var actualCalls = targetInterceptor.InterceptionContext.ActualInvocations.ToList(); - var callCount = actualCalls.Where(ac => expected.Matches(ac)).Count(); - if (!times.Verify(callCount)) - { + var callCount = actualCalls.Where(ac => expected.Matches(ac)).Count(); + if (!times.Verify(callCount)) + { var setups = targetInterceptor.InterceptionContext.OrderedCalls.Where(oc => AreSameMethod(oc.SetupExpression, expression)); - ThrowVerifyException(expected, setups, actualCalls, expression, times, callCount); + ThrowVerifyException(expected, setups, actualCalls, expression, times, callCount); } } @@ -392,8 +391,8 @@ private static void ThrowVerifyException( int callCount) { var message = times.GetExceptionMessage(expected.FailMessage, expression.PartialMatcherAwareEval().ToLambda().ToStringFixed(), callCount) + - Environment.NewLine + FormatSetupsInfo(setups) + - Environment.NewLine + FormatInvocations(actualCalls); + Environment.NewLine + FormatSetupsInfo(setups) + + Environment.NewLine + FormatInvocations(actualCalls); throw new MockException(MockException.ExceptionReason.VerificationFailed, message); } @@ -403,9 +402,9 @@ private static string FormatSetupsInfo(IEnumerable setups) .Select(s => s.SetupExpression.PartialMatcherAwareEval().ToLambda().ToStringFixed() + ", " + FormatCallCount(s.CallCount)) .ToArray(); - return expressionSetups.Length == 0 ? - "No setups configured." : - Environment.NewLine + "Configured setups:" + Environment.NewLine + string.Join(Environment.NewLine, expressionSetups); + return expressionSetups.Length == 0 + ? "No setups configured." + : Environment.NewLine + "Configured setups:" + Environment.NewLine + string.Join(Environment.NewLine, expressionSetups); } private static string FormatCallCount(int callCount) @@ -429,23 +428,23 @@ private static string FormatInvocations(IEnumerable invocations) .Select(i => i.Format()) .ToArray(); - return formattedInvocations.Length == 0 ? - "No invocations performed." : - Environment.NewLine + "Performed invocations:" + Environment.NewLine + string.Join(Environment.NewLine, formattedInvocations); + return formattedInvocations.Length == 0 + ? "No invocations performed." + : Environment.NewLine + "Performed invocations:" + Environment.NewLine + string.Join(Environment.NewLine, formattedInvocations); } #endregion #region Setup - internal static MethodCall Setup(Mock mock, Expression> expression, Condition condition) - where T : class - { - return PexProtector.Invoke(() => - { - var methodCall = expression.GetCallInfo(mock); - var method = methodCall.Method; - var args = methodCall.Arguments.ToArray(); + internal static MethodCall Setup(Mock mock, Expression> expression, Condition condition) + where T : class + { + return PexProtector.Invoke(() => + { + var methodCall = expression.GetCallInfo(mock); + var method = methodCall.Method; + var args = methodCall.Arguments.ToArray(); ThrowIfNotMember(expression, method); ThrowIfCantOverride(expression, method); @@ -459,18 +458,18 @@ internal static MethodCall Setup(Mock mock, Expression> expre }); } - internal static MethodCallReturn Setup( - Mock mock, - Expression> expression, - Condition condition) - where T : class - { - return PexProtector.Invoke(() => - { - if (expression.IsProperty()) - { - return SetupGet(mock, expression, condition); - } + internal static MethodCallReturn Setup( + Mock mock, + Expression> expression, + Condition condition) + where T : class + { + return PexProtector.Invoke(() => + { + if (expression.IsProperty()) + { + return SetupGet(mock, expression, condition); + } var methodCall = expression.GetCallInfo(mock); var method = methodCall.Method; @@ -488,19 +487,19 @@ internal static MethodCallReturn Setup( }); } - internal static MethodCallReturn SetupGet( - Mock mock, - Expression> expression, - Condition condition) - where T : class - { - return PexProtector.Invoke(() => - { - if (expression.IsPropertyIndexer()) - { - // Treat indexers as regular method invocations. - return Setup(mock, expression, condition); - } + internal static MethodCallReturn SetupGet( + Mock mock, + Expression> expression, + Condition condition) + where T : class + { + return PexProtector.Invoke(() => + { + if (expression.IsPropertyIndexer()) + { + // Treat indexers as regular method invocations. + return Setup(mock, expression, condition); + } var prop = expression.ToPropertyInfo(); ThrowIfPropertyNotReadable(prop); @@ -510,7 +509,7 @@ internal static MethodCallReturn SetupGet( var call = new MethodCallReturn(mock, condition, expression, propGet, new Expression[0]); // Directly casting to MemberExpression is fine as ToPropertyInfo would throw if it wasn't - var targetInterceptor = GetInterceptor(((MemberExpression)expression.Body).Expression, mock); + var targetInterceptor = GetInterceptor(((MemberExpression) expression.Body).Expression, mock); targetInterceptor.AddCall(call, SetupKind.Other); @@ -518,42 +517,42 @@ internal static MethodCallReturn SetupGet( }); } - internal static SetterMethodCall SetupSet( - Mock mock, - Action setterExpression, - Condition condition) - where T : class - { - return PexProtector.Invoke(() => - { - return SetupSetImpl>( - mock, - setterExpression, - (m, expr, method, value) => - { - var call = new SetterMethodCall(m, condition, expr, method, value[0]); - m.Interceptor.AddCall(call, SetupKind.PropertySet); - return call; - }); - }); - } - - internal static MethodCall SetupSet(Mock mock, Action setterExpression, Condition condition) - where T : class - { - return PexProtector.Invoke(() => - { - return SetupSetImpl>( - mock, - setterExpression, - (m, expr, method, values) => - { - var call = new MethodCall(m, condition, expr, method, values); - m.Interceptor.AddCall(call, SetupKind.PropertySet); - return call; - }); - }); - } + internal static SetterMethodCall SetupSet( + Mock mock, + Action setterExpression, + Condition condition) + where T : class + { + return PexProtector.Invoke(() => + { + return SetupSetImpl>( + mock, + setterExpression, + (m, expr, method, value) => + { + var call = new SetterMethodCall(m, condition, expr, method, value[0]); + m.Interceptor.AddCall(call, SetupKind.PropertySet); + return call; + }); + }); + } + + internal static MethodCall SetupSet(Mock mock, Action setterExpression, Condition condition) + where T : class + { + return PexProtector.Invoke(() => + { + return SetupSetImpl>( + mock, + setterExpression, + (m, expr, method, values) => + { + var call = new MethodCall(m, condition, expr, method, values); + m.Interceptor.AddCall(call, SetupKind.PropertySet); + return call; + }); + }); + } internal static SetterMethodCall SetupSet( Mock mock, @@ -567,7 +566,7 @@ internal static SetterMethodCall SetupSet( ThrowIfCantOverride(expression, propSet); var call = new SetterMethodCall(mock, expression, propSet); - var targetInterceptor = GetInterceptor(((MemberExpression)expression.Body).Expression, mock); + var targetInterceptor = GetInterceptor(((MemberExpression) expression.Body).Expression, mock); targetInterceptor.AddCall(call, SetupKind.PropertySet); @@ -602,11 +601,11 @@ private static TCall SetupSetImpl( // No need to call ThrowIfCantOverride as non-overridable would have thrown above already. - // Get the variable name as used in the actual delegate :) - // because of delegate currying, look at the last parameter for the Action's backing method, not the first - var setterExpressionParameters = setterExpression.Method.GetParameters(); - var parameterName = setterExpressionParameters[setterExpressionParameters.Length - 1].Name; - var x = Expression.Parameter(last.Invocation.Method.DeclaringType, parameterName); + // Get the variable name as used in the actual delegate :) + // because of delegate currying, look at the last parameter for the Action's backing method, not the first + var setterExpressionParameters = setterExpression.Method.GetParameters(); + var parameterName = setterExpressionParameters[setterExpressionParameters.Length - 1].Name; + var x = Expression.Parameter(last.Invocation.Method.DeclaringType, parameterName); var arguments = last.Invocation.Arguments; var parameters = setter.GetParameters(); @@ -621,7 +620,7 @@ private static TCall SetupSetImpl( } var lambda = Expression.Lambda( - typeof(Action<>).MakeGenericType(x.Type), + typeof (Action<>).MakeGenericType(x.Type), Expression.Call(x, last.Invocation.Method, values), x); @@ -657,7 +656,7 @@ private static TCall SetupSetImpl( } var lambda = Expression.Lambda( - typeof(Action<>).MakeGenericType(x.Type), + typeof (Action<>).MakeGenericType(x.Type), Expression.Call(x, last.Invocation.Method, values), x); @@ -677,56 +676,79 @@ private static Expression GetValueExpression(object value, Type type) return Expression.Convert(Expression.Constant(value), type); } - internal static void SetupAllProperties(Mock mock) - { - PexProtector.Invoke(() => - { - var mockType = mock.MockedType; - var properties = mockType.GetProperties() - .Concat(mockType.GetInterfaces().SelectMany(i => i.GetProperties())) - .Where(p => - p.CanRead && p.CanOverrideGet() && - p.GetIndexParameters().Length == 0 && - p.PropertyType != mockType && - !(p.CanWrite ^ (p.CanWrite & p.CanOverrideSet()))) - .Distinct(); - - var setupPropertyMethod = mock.GetType().GetMethods() - .First(m => m.Name == "SetupProperty" && m.GetParameters().Length == 2); - var setupGetMethod = mock.GetType().GetMethods() - .First(m => m.Name == "SetupGet" && m.GetParameters().Length == 1); - - foreach (var property in properties) + internal static void SetupAllProperties(Mock mock) + { + PexProtector.Invoke(() => + { + var mockedTypesStack = new Stack(); + SetupAllProperties(mock, mockedTypesStack); + }); + } + + private static void SetupAllProperties(Mock mock, Stack mockedTypesStack) + { + var mockType = mock.MockedType; + mockedTypesStack.Push(mockType); + var properties = mockType.GetProperties() + .Concat(mockType.GetInterfaces().SelectMany(i => i.GetProperties())) + .Where(p => + p.CanRead && p.CanOverrideGet() && + p.GetIndexParameters().Length == 0 && + !(p.CanWrite ^ (p.CanWrite & p.CanOverrideSet()))) + .Distinct(); + + var setupPropertyMethod = mock.GetType().GetMethods() + .First(m => m.Name == "SetupProperty" && m.GetParameters().Length == 2); + var setupGetMethod = mock.GetType().GetMethods() + .First(m => m.Name == "SetupGet" && m.GetParameters().Length == 1); + + foreach (var property in properties) + { + var expression = GetPropertyExpression(mockType, property); + object initialValue = GetInitialValue(mock.DefaultValueProvider, mockedTypesStack, property); + + var mocked = initialValue as IMocked; + if (mocked != null) { - var expression = GetPropertyExpression(mockType, property); - var initialValue = mock.DefaultValueProvider.ProvideDefault(property.GetGetMethod()); + SetupAllProperties(mocked.Mock, mockedTypesStack); + } - var mocked = initialValue as IMocked; - if (mocked != null) - { - SetupAllProperties(mocked.Mock); - } + if (property.CanWrite) + { + setupPropertyMethod.MakeGenericMethod(property.PropertyType) + .Invoke(mock, new[] {expression, initialValue}); + } + else + { + var genericSetupGetMethod = setupGetMethod.MakeGenericMethod(property.PropertyType); + var returnsMethod = + genericSetupGetMethod + .ReturnType + .GetInterface("IReturnsGetter`2", ignoreCase: false) + .GetMethod("Returns", new Type[] {property.PropertyType}); + + var returnsGetter = genericSetupGetMethod.Invoke(mock, new[] {expression}); + returnsMethod.Invoke(returnsGetter, new[] {initialValue}); + } + } + } - if (property.CanWrite) - { - setupPropertyMethod.MakeGenericMethod(property.PropertyType) - .Invoke(mock, new[] { expression, initialValue }); - } - else - { - var genericSetupGetMethod = setupGetMethod.MakeGenericMethod(property.PropertyType); - var returnsMethod = - genericSetupGetMethod - .ReturnType - .GetInterface("IReturnsGetter`2", ignoreCase: false) - .GetMethod("Returns", new Type[] { property.PropertyType }); - - var returnsGetter = genericSetupGetMethod.Invoke(mock, new[] { expression }); - returnsMethod.Invoke(returnsGetter, new[] { initialValue }); - } - } - }); - } + private static object GetInitialValue(IDefaultValueProvider valueProvider, Stack mockedTypesStack, PropertyInfo property) + { + if (mockedTypesStack.Contains(property.PropertyType)) + { + // to deal with loops in the property graph + valueProvider = new EmptyDefaultValueProvider(); + } + else + { + // to make sure that properties of types that don't impelemt ISerializable properly (Castle throws ArgumentException) + // are mocked with default value instead. + // It will only result in exception if the properties are accessed. + valueProvider = new SerializableTypesValueProvider(valueProvider); + } + return valueProvider.ProvideDefault(property.GetGetMethod()); + } private static Expression GetPropertyExpression(Type mockType, PropertyInfo property) { @@ -741,13 +763,14 @@ private static Expression GetPropertyExpression(Type mockType, PropertyInfo prop private static Interceptor GetInterceptor(Expression fluentExpression, Mock mock) { var targetExpression = FluentMockVisitor.Accept(fluentExpression, mock); - var targetLambda = Expression.Lambda>(Expression.Convert(targetExpression, typeof(Mock))); + var targetLambda = Expression.Lambda>(Expression.Convert(targetExpression, typeof (Mock))); var targetObject = targetLambda.Compile()(); return targetObject.Interceptor; } - [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "This is a helper method for the one receiving the expression.")] + [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", + Justification = "This is a helper method for the one receiving the expression.")] private static void ThrowIfPropertyNotWritable(PropertyInfo prop) { if (!prop.CanWrite) @@ -816,21 +839,22 @@ private static void ThrowIfCantOverride(MethodBase setter) where T : class throw new NotSupportedException(string.Format( CultureInfo.CurrentCulture, Resources.SetupOnNonOverridableMember, - typeof(T).Name + "." + setter.Name.Substring(4))); + typeof (T).Name + "." + setter.Name.Substring(4))); } } private class FluentMockVisitor : ExpressionVisitor { - static readonly MethodInfo FluentMockGenericMethod = ((Func, Expression>, Mock>) - QueryableMockExtensions.FluentMock).Method.GetGenericMethodDefinition(); - static readonly MethodInfo MockGetGenericMethod = ((Func>)Moq.Mock.Get) + private static readonly MethodInfo FluentMockGenericMethod = ((Func, Expression>, Mock>) + QueryableMockExtensions.FluentMock).Method.GetGenericMethodDefinition(); + + private static readonly MethodInfo MockGetGenericMethod = ((Func>) Get) .Method.GetGenericMethodDefinition(); - Expression expression; - Mock mock; + private readonly Expression expression; + private readonly Mock mock; - public FluentMockVisitor(Expression expression, Mock mock) + private FluentMockVisitor(Expression expression, Mock mock) { this.expression = expression; this.mock = mock; @@ -841,7 +865,7 @@ public static Expression Accept(Expression expression, Mock mock) return new FluentMockVisitor(expression, mock).Accept(); } - public Expression Accept() + private Expression Accept() { return Visit(expression); } @@ -885,11 +909,11 @@ protected override Expression VisitMember(MemberExpression node) // compiler-generated types as they are typically the // anonymous types generated to build up the query expressions. if (node.Expression.NodeType == ExpressionType.Parameter && - node.Expression.Type.GetCustomAttribute(false) != null) + node.Expression.Type.GetCustomAttribute(false) != null) { - var memberType = node.Member is FieldInfo ? - ((FieldInfo)node.Member).FieldType : - ((PropertyInfo)node.Member).PropertyType; + var memberType = node.Member is FieldInfo + ? ((FieldInfo) node.Member).FieldType + : ((PropertyInfo) node.Member).PropertyType; // Generate a Mock.Get over the entire member access rather. // .foo => Mock.Get(.foo) @@ -903,9 +927,9 @@ protected override Expression VisitMember(MemberExpression node) var lambdaParam = Expression.Parameter(node.Expression.Type, "mock"); Expression lambdaBody = Expression.MakeMemberAccess(lambdaParam, node.Member); - var targetMethod = GetTargetMethod(node.Expression.Type, ((PropertyInfo)node.Member).PropertyType); + var targetMethod = GetTargetMethod(node.Expression.Type, ((PropertyInfo) node.Member).PropertyType); - return TranslateFluent(node.Expression.Type, ((PropertyInfo)node.Member).PropertyType, targetMethod, Visit(node.Expression), lambdaParam, lambdaBody); + return TranslateFluent(node.Expression.Type, ((PropertyInfo) node.Member).PropertyType, targetMethod, Visit(node.Expression), lambdaParam, lambdaBody); } private static Expression TranslateFluent( @@ -916,7 +940,7 @@ private static Expression TranslateFluent( ParameterExpression lambdaParam, Expression lambdaBody) { - var funcType = typeof(Func<,>).MakeGenericType(objectType, returnType); + var funcType = typeof (Func<,>).MakeGenericType(objectType, returnType); // This is the fluent extension method one, so pass the instance as one more arg. return Expression.Call( @@ -926,8 +950,8 @@ private static Expression TranslateFluent( funcType, lambdaBody, lambdaParam - ) - ); + ) + ); } private static MethodInfo GetTargetMethod(Type objectType, Type returnType) @@ -952,11 +976,11 @@ internal void DoRaise(EventInfo ev, EventArgs args) throw new InvalidOperationException(Resources.RaisedUnassociatedEvent); } - foreach (var del in this.Interceptor.InterceptionContext.GetInvocationList(ev).ToArray()) - { - del.InvokePreserveStack(this.Object, args); - } - } + foreach (var del in this.Interceptor.InterceptionContext.GetInvocationList(ev).ToArray()) + { + del.InvokePreserveStack(this.Object, args); + } + } /// /// Raises the associated event with the given @@ -970,35 +994,37 @@ internal void DoRaise(EventInfo ev, params object[] args) } foreach (var del in this.Interceptor.InterceptionContext.GetInvocationList(ev).ToArray()) - { - // Non EventHandler-compatible delegates get the straight - // arguments, not the typical "sender, args" arguments. - del.InvokePreserveStack(args); - } - } + { + // Non EventHandler-compatible delegates get the straight + // arguments, not the typical "sender, args" arguments. + del.InvokePreserveStack(args); + } + } #endregion #region As /// - [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "As", Justification = "We want the method called exactly as the keyword because that's what it does, it adds an implemented interface so that you can cast it later.")] + [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "As", + Justification = + "We want the method called exactly as the keyword because that's what it does, it adds an implemented interface so that you can cast it later.")] public virtual Mock As() where TInterface : class { - if (this.isInitialized && !this.ImplementedInterfaces.Contains(typeof(TInterface))) + if (this.isInitialized && !this.ImplementedInterfaces.Contains(typeof (TInterface))) { throw new InvalidOperationException(Resources.AlreadyInitialized); } - if (!typeof(TInterface).IsInterface) + if (!typeof (TInterface).IsInterface) { throw new ArgumentException(Resources.AsMustBeInterface); } - if (!this.ImplementedInterfaces.Contains(typeof(TInterface))) + if (!this.ImplementedInterfaces.Contains(typeof (TInterface))) { - this.ImplementedInterfaces.Add(typeof(TInterface)); + this.ImplementedInterfaces.Add(typeof (TInterface)); } return new AsInterface(this); diff --git a/Source/Moq.csproj b/Source/Moq.csproj index d9b410dbc..ef9032ee3 100644 --- a/Source/Moq.csproj +++ b/Source/Moq.csproj @@ -104,6 +104,7 @@ + diff --git a/Source/SerializableTypesValueProvider.cs b/Source/SerializableTypesValueProvider.cs new file mode 100644 index 000000000..de1611705 --- /dev/null +++ b/Source/SerializableTypesValueProvider.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using System.Runtime.Serialization; + + +namespace Moq +{ + /// + /// A that returns an empty default value + /// for serializable types that do not implement properly, + /// and returns the value provided by the decorated provider otherwise. + /// + internal class SerializableTypesValueProvider : IDefaultValueProvider + { + private readonly IDefaultValueProvider decorated; + private readonly EmptyDefaultValueProvider emptyDefaultValueProvider = new EmptyDefaultValueProvider(); + + public SerializableTypesValueProvider(IDefaultValueProvider decorated) + { + this.decorated = decorated; + } + + public void DefineDefault(T value) + { + decorated.DefineDefault(value); + } + + public object ProvideDefault(MethodInfo member) + { + return !member.ReturnType.IsSerializable || member.ReturnType.IsSerializableMockable() + ? decorated.ProvideDefault(member) + : emptyDefaultValueProvider.ProvideDefault(member); + } + } +} \ No newline at end of file diff --git a/UnitTests/Moq.Tests.csproj b/UnitTests/Moq.Tests.csproj index cb132f31d..1aed1d809 100644 --- a/UnitTests/Moq.Tests.csproj +++ b/UnitTests/Moq.Tests.csproj @@ -60,6 +60,7 @@ + diff --git a/UnitTests/Regressions/FluentMockIssues.cs b/UnitTests/Regressions/FluentMockIssues.cs new file mode 100644 index 000000000..c8a0fabf8 --- /dev/null +++ b/UnitTests/Regressions/FluentMockIssues.cs @@ -0,0 +1,73 @@ +using System; +using System.Runtime.Serialization; + +using Xunit; + + +namespace Moq.Tests.Regressions +{ + public class FluentMockIssues + { + public interface IOne + { + ITwo Two { get; } + } + + public interface ITwo + { + IThree Three { get; } + } + + public interface IThree + { + ITwo LoopBack { get; } + string SomeString { get; } + } + + [Fact] + public void CyclesInThePropertyGraphAreHandled() + { + var foo = new Mock {DefaultValue = DefaultValue.Mock}; + foo.SetupGet(m => m.Two.Three.SomeString).Returns("blah"); + + // the default value of the loopback property is not mocked + Assert.Null(foo.Object.Two.Three.LoopBack); + foo.SetupGet(m => m.Two.Three.LoopBack).Returns(Mock.Of()); + Assert.NotNull(foo.Object.Two.Three.LoopBack); + } + + [Fact] + public void SerializableTypesNotImplementingISerializableProperlyNotMockable() + { + var mock = new Mock {DefaultValue = DefaultValue.Mock}; + // c.Serializable can't be mocked in a standard way as it doesn't implement the ISerializable properly + Assert.Throws(() => mock.SetupGet(c => c.Serializable.SomeString).Returns("blah")); + } + + [Fact] + public void SerializableTypesNotImplementingISerializableProperlySetToDefaultValue() + { + var mock = new Mock {DefaultValue = DefaultValue.Mock}; + mock.SetupGet(c => c.SomeString).Returns("blah"); + + Assert.Equal("blah", mock.Object.SomeString); + Assert.Throws(() => mock.Object.Serializable); + } + + public interface IContainingSerializableProperties + { + SerializableWithoutDeserializationConstructor Serializable { get; } + string SomeString { get; } + } + + [Serializable] + public abstract class SerializableWithoutDeserializationConstructor : ISerializable + { + public virtual void GetObjectData(SerializationInfo info, StreamingContext context) + { + } + + public virtual string SomeString { get; set; } + } + } +} From 570d0feffa22688043a8b8d693941d97892a454c Mon Sep 17 00:00:00 2001 From: Martin Hammerschmied Date: Fri, 12 Aug 2016 16:51:11 +0200 Subject: [PATCH 05/13] Fixed `InterceptObjectMethodsMixin` to properly check for Sustem.Object methods in the interceptor context. Fixes #273 (again :-)) (#280) --- Source/InterceptorStrategies.cs | 6 +++--- UnitTests/Regressions/IssueReportsFixture.cs | 13 ++++++++----- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/Source/InterceptorStrategies.cs b/Source/InterceptorStrategies.cs index 6521b31cc..b4eeda6b9 100644 --- a/Source/InterceptorStrategies.cs +++ b/Source/InterceptorStrategies.cs @@ -143,21 +143,21 @@ public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorCo var method = invocation.Method; // Only if there is no corresponding setup for `ToString()` - if (IsObjectMethod(method, "ToString") && !ctx.OrderedCalls.Select(c => IsObjectMethod(c.Method, "ToString")).Any()) + if (IsObjectMethod(method, "ToString") && !ctx.OrderedCalls.Any(c => IsObjectMethod(c.Method, "ToString"))) { invocation.ReturnValue = ctx.Mock.ToString() + ".Object"; return InterceptionAction.Stop; } // Only if there is no corresponding setup for `GetHashCode()` - if (IsObjectMethod(method, "GetHashCode") && !ctx.OrderedCalls.Select(c => IsObjectMethod(c.Method, "GetHashCode")).Any()) + if (IsObjectMethod(method, "GetHashCode") && !ctx.OrderedCalls.Any(c => IsObjectMethod(c.Method, "GetHashCode"))) { invocation.ReturnValue = ctx.Mock.GetHashCode(); return InterceptionAction.Stop; } // Only if there is no corresponding setup for `Equals()` - if (IsObjectMethod(method, "Equals") && !ctx.OrderedCalls.Select(c => IsObjectMethod(c.Method, "Equals")).Any()) + if (IsObjectMethod(method, "Equals") && !ctx.OrderedCalls.Any(c => IsObjectMethod(c.Method, "Equals"))) { invocation.ReturnValue = ReferenceEquals(invocation.Arguments.First(), ctx.Mock.Object); return InterceptionAction.Stop; diff --git a/UnitTests/Regressions/IssueReportsFixture.cs b/UnitTests/Regressions/IssueReportsFixture.cs index 05a2e2b99..9682029b4 100644 --- a/UnitTests/Regressions/IssueReportsFixture.cs +++ b/UnitTests/Regressions/IssueReportsFixture.cs @@ -285,16 +285,19 @@ public class Issue273 [Fact] public void SystemObjectMethodsShouldWorkInStrictMocks() { - var mockObject = new Mock(MockBehavior.Strict).Object; + var mock = new Mock(MockBehavior.Strict); - Assert.IsType(typeof(int), mockObject.GetHashCode()); - Assert.IsType(typeof(string), mockObject.ToString()); - Assert.False(mockObject.Equals("ImNotTheObject")); - Assert.True(mockObject.Equals(mockObject)); + mock.Setup(x => x.Test()).Returns(true); + + Assert.IsType(typeof(int), mock.Object.GetHashCode()); + Assert.IsType(typeof(string), mock.Object.ToString()); + Assert.False(mock.Object.Equals("ImNotTheObject")); + Assert.True(mock.Object.Equals(mock.Object)); } public interface IMyInterface { + bool Test(); } } From 756bf4e3e7b213c6d819ae7acd5e04280a07a040 Mon Sep 17 00:00:00 2001 From: anilkamath87 Date: Fri, 12 Aug 2016 07:52:52 -0700 Subject: [PATCH 06/13] Moq Reset should clear all existing calls in the intereceptor (#277) https://github.com/Moq/moq4/issues/220 --- Source/Interceptor.cs | 5 ++++ Source/MockExtensions.cs | 1 + UnitTests/ExtensionsFixture.cs | 50 ++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/Source/Interceptor.cs b/Source/Interceptor.cs index 324e628df..c662798bb 100644 --- a/Source/Interceptor.cs +++ b/Source/Interceptor.cs @@ -113,6 +113,11 @@ public void AddCall(IProxyCall call, SetupKind kind) InterceptionContext.AddOrderedCall(call); } + internal void ClearCalls() + { + calls.Clear(); + } + private IEnumerable InterceptionStrategies() { yield return new HandleDestructor(); diff --git a/Source/MockExtensions.cs b/Source/MockExtensions.cs index cc5b0606b..f47230723 100644 --- a/Source/MockExtensions.cs +++ b/Source/MockExtensions.cs @@ -35,6 +35,7 @@ public static void Reset(this Mock mock) { mock.Interceptor.InterceptionContext.ClearOrderedCalls(); mock.Interceptor.InterceptionContext.ClearEventHandlers(); + mock.Interceptor.ClearCalls(); mock.ResetCalls(); } } diff --git a/UnitTests/ExtensionsFixture.cs b/UnitTests/ExtensionsFixture.cs index 77da87bee..7219fcdce 100644 --- a/UnitTests/ExtensionsFixture.cs +++ b/UnitTests/ExtensionsFixture.cs @@ -38,6 +38,56 @@ public void SetupDoesNotApplyAfterMockWasReset() var result = mock.Object.Execute("ping"); Assert.Null(result); } + + [Fact] + + public void Loose() + { + var myMock = new Mock>(MockBehavior.Loose); + myMock + .Setup(a => a.ToString()) + .Returns("Hello"); + myMock.Reset(); + Assert.NotEqual("Hello", myMock.Object.ToString()); + myMock.VerifyAll(); + } + + [Fact] + + public void Strict() + { + var myMock = new Mock>(MockBehavior.Strict); + myMock + .Setup(a => a.ToString()) + .Returns("Hello"); + myMock.Reset(); + Assert.NotEqual("Hello", myMock.Object.ToString()); + myMock.VerifyAll(); + } + + [Fact] + + public void LooseNoCall() + { + var myMock = new Mock>(MockBehavior.Loose); + myMock + .Setup(a => a.ToString()) + .Returns("Hello"); + myMock.Reset(); + myMock.VerifyAll(); + } + + [Fact] + + public void StrictNoCall() + { + var myMock = new Mock>(MockBehavior.Strict); + myMock + .Setup(a => a.ToString()) + .Returns("Hello"); + myMock.Reset(); + myMock.VerifyAll(); + } #endregion } From cd67f5cc7cb8412350c3078c089e6f44a36cd44e Mon Sep 17 00:00:00 2001 From: bewaretherobots Date: Tue, 20 Sep 2016 14:36:55 -0700 Subject: [PATCH 07/13] Fix for issue #228: Events not properly raised when mocked type implements multiple interfaces (#288) --- Source/InterceptorStrategies.cs | 31 ++++-------- Source/Mock.Generic.cs | 1 + Source/Mock.cs | 10 +++- UnitTests/Regressions/IssueReportsFixture.cs | 53 ++++++++++++++++++++ 4 files changed, 71 insertions(+), 24 deletions(-) diff --git a/Source/InterceptorStrategies.cs b/Source/InterceptorStrategies.cs index b4eeda6b9..00f58b30f 100644 --- a/Source/InterceptorStrategies.cs +++ b/Source/InterceptorStrategies.cs @@ -208,38 +208,25 @@ internal class AddActualInvocation : IInterceptStrategy /// Name of the event, with the set_ or get_ prefix already removed private EventInfo GetEventFromName(string eventName) { - var depthFirstProgress = new Queue(ctx.Mock.ImplementedInterfaces.Skip(1)); - depthFirstProgress.Enqueue(ctx.TargetType); - while (depthFirstProgress.Count > 0) - { - var currentType = depthFirstProgress.Dequeue(); - var eventInfo = currentType.GetEvent(eventName); - if (eventInfo != null) - { - return eventInfo; - } - - foreach (var implementedType in GetAncestorTypes(currentType)) - { - depthFirstProgress.Enqueue(implementedType); - } - } - return GetNonPublicEventFromName(eventName); + return GetEventFromName(eventName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public) ?? + GetEventFromName(eventName, BindingFlags.Instance | BindingFlags.NonPublic); } /// /// Get an eventInfo for a given event name. Search type ancestors depth first if necessary. - /// Searches also in non public events. + /// Searches events using the specified binding constraints. /// /// Name of the event, with the set_ or get_ prefix already removed - private EventInfo GetNonPublicEventFromName(string eventName) + /// Specifies how the search for events is conducted + private EventInfo GetEventFromName(string eventName, BindingFlags bindingAttr) { - var depthFirstProgress = new Queue(ctx.Mock.ImplementedInterfaces.Skip(1)); + // Ignore internally implemented interfaces + var depthFirstProgress = new Queue(ctx.Mock.ImplementedInterfaces.Skip(ctx.Mock.InternallyImplementedInterfaceCount)); depthFirstProgress.Enqueue(ctx.TargetType); while (depthFirstProgress.Count > 0) { var currentType = depthFirstProgress.Dequeue(); - var eventInfo = currentType.GetEvent(eventName, BindingFlags.Instance | BindingFlags.NonPublic); + var eventInfo = currentType.GetEvent(eventName, bindingAttr); if (eventInfo != null) { return eventInfo; @@ -254,7 +241,6 @@ private EventInfo GetNonPublicEventFromName(string eventName) return null; } - /// /// Given a type return all of its ancestors, both types and interfaces. /// @@ -269,6 +255,7 @@ private static IEnumerable GetAncestorTypes(Type initialType) return initialType.GetInterfaces(); } + InterceptorContext ctx; public InterceptionAction HandleIntercept(ICallContext invocation, InterceptorContext ctx, CurrentInterceptContext localctx) { diff --git a/Source/Mock.Generic.cs b/Source/Mock.Generic.cs index f6d0a0b1d..d0c4cbeb5 100644 --- a/Source/Mock.Generic.cs +++ b/Source/Mock.Generic.cs @@ -116,6 +116,7 @@ public Mock(MockBehavior behavior, params object[] args) this.constructorArguments = args; this.ImplementedInterfaces.AddRange(typeof(T).GetInterfaces().Where(i => (i.IsPublic || i.IsNestedPublic) && !i.IsImport)); this.ImplementedInterfaces.Add(typeof(IMocked)); + this.InternallyImplementedInterfaceCount = this.ImplementedInterfaces.Count; this.CheckParameters(); } diff --git a/Source/Mock.cs b/Source/Mock.cs index 3ed5ca1a2..bd8161617 100644 --- a/Source/Mock.cs +++ b/Source/Mock.cs @@ -108,8 +108,8 @@ public static Mock Get(T mocked) where T : class var types = string.Join( ", ", new[] {mockedType} - // Skip first interface which is always our internal IMocked - .Concat(mock.ImplementedInterfaces.Skip(1)) + // Ignore internally defined IMocked + .Concat(mock.ImplementedInterfaces.Where(t => t != imockedType)) .Select(t => t.Name) .ToArray()); @@ -224,6 +224,12 @@ internal IDefaultValueProvider DefaultValueProvider /// internal List ImplementedInterfaces { get; private set; } + /// + /// Indicates the number of interfaces in that were + /// defined internally, rather than through calls to . + /// + internal protected int InternallyImplementedInterfaceCount { get; protected set; } + #region Verify /// diff --git a/UnitTests/Regressions/IssueReportsFixture.cs b/UnitTests/Regressions/IssueReportsFixture.cs index 9682029b4..f99f4b0fb 100644 --- a/UnitTests/Regressions/IssueReportsFixture.cs +++ b/UnitTests/Regressions/IssueReportsFixture.cs @@ -1523,6 +1523,59 @@ public virtual int DoGeneric(int value) #endregion + #region #228 + + public class _228 + { + [Fact] + public void Test() + { + var mock = new Mock() { CallBase = true }; + IFoo foo = mock.Object; + IBar bar = mock.Object; + + bool fooRaised = false; + bool barRaised = false; + foo.Foo += (s, e) => fooRaised = true; + bar.Bar += (s, e) => barRaised = true; + + mock.Object.RaiseMyEvents(); + + Assert.True(fooRaised); + Assert.True(barRaised); + } + + public interface IFoo + { + event EventHandler Foo; + } + + public interface IBar + { + event EventHandler Bar; + } + + public class FooBar : IFoo, IBar + { + public event EventHandler Foo; + public event EventHandler Bar; + + public void RaiseMyEvents() + { + if (this.Foo != null) + { + this.Foo(this, EventArgs.Empty); + } + if (this.Bar != null) + { + this.Bar(this, EventArgs.Empty); + } + } + } + } + + #endregion + #region #229 public class _229 From 8772a49d4565a03d8455685f5633ffed80bb2cc9 Mon Sep 17 00:00:00 2001 From: Matthew Kubicki Date: Tue, 11 Oct 2016 22:18:49 +0100 Subject: [PATCH 08/13] Fixes #278. Only fixes some of the issue though. Also adds new VB test assembly, hopefully in a way people will be happy with (#291) --- Moq.VisualBasicTests/IssueReports.vb | 29 +++++ .../Moq.VisualBasicTests.vbproj | 123 ++++++++++++++++++ .../My Project/Application.Designer.vb | 13 ++ .../My Project/Application.myapp | 10 ++ .../My Project/AssemblyInfo.vb | 35 +++++ .../My Project/Resources.Designer.vb | 63 +++++++++ .../My Project/Resources.resx | 117 +++++++++++++++++ .../My Project/Settings.Designer.vb | 73 +++++++++++ .../My Project/Settings.settings | 7 + Moq.VisualBasicTests/project.json | 13 ++ Moq.sln | 10 ++ Source/Interceptor.cs | 13 +- UnitTests/MockedEventsFixture.cs | 3 +- 13 files changed, 503 insertions(+), 6 deletions(-) create mode 100644 Moq.VisualBasicTests/IssueReports.vb create mode 100644 Moq.VisualBasicTests/Moq.VisualBasicTests.vbproj create mode 100644 Moq.VisualBasicTests/My Project/Application.Designer.vb create mode 100644 Moq.VisualBasicTests/My Project/Application.myapp create mode 100644 Moq.VisualBasicTests/My Project/AssemblyInfo.vb create mode 100644 Moq.VisualBasicTests/My Project/Resources.Designer.vb create mode 100644 Moq.VisualBasicTests/My Project/Resources.resx create mode 100644 Moq.VisualBasicTests/My Project/Settings.Designer.vb create mode 100644 Moq.VisualBasicTests/My Project/Settings.settings create mode 100644 Moq.VisualBasicTests/project.json diff --git a/Moq.VisualBasicTests/IssueReports.vb b/Moq.VisualBasicTests/IssueReports.vb new file mode 100644 index 000000000..a520e286c --- /dev/null +++ b/Moq.VisualBasicTests/IssueReports.vb @@ -0,0 +1,29 @@ +Imports Moq +Imports Xunit + + + +Public Class IssueReports + + Public Class Issue278 + + + Public Sub SetupsForPropertiesWithMultipleArgsDoNotOverwriteEachOther() + Dim mock As New Mock(Of ISimpleInterface)() + + mock.Setup(Function(m) m.PropertyWithMultipleArgs(1, 1)).Returns(1) + mock.Setup(Function(m) m.PropertyWithMultipleArgs(1, 2)).Returns(2) + + Assert.Equal(1, mock.Object.PropertyWithMultipleArgs(1, 1)) + Assert.Equal(2, mock.Object.PropertyWithMultipleArgs(1, 2)) + + End Sub + + Public Interface ISimpleInterface + + ReadOnly Property PropertyWithMultipleArgs(setting As Integer, setting2 As Integer) As Integer + + End Interface + End Class + +End Class diff --git a/Moq.VisualBasicTests/Moq.VisualBasicTests.vbproj b/Moq.VisualBasicTests/Moq.VisualBasicTests.vbproj new file mode 100644 index 000000000..74987e9bc --- /dev/null +++ b/Moq.VisualBasicTests/Moq.VisualBasicTests.vbproj @@ -0,0 +1,123 @@ + + + + + Debug + AnyCPU + {95F2E2B2-2E06-4AC7-A86F-9B1B6369D4AD} + Library + Moq.VisualBasicTests + Moq.VisualBasicTests + 512 + Windows + v4.5 + true + + + true + full + true + true + bin\Debug\ + Moq.VisualBasicTests.xml + 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022 + + + pdbonly + false + true + true + bin\Release\ + Moq.VisualBasicTests.xml + 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022 + + + On + + + Binary + + + On + + + On + + + + {1C91AC30-5977-4BE5-BA67-8EB186C03514} + Moq + + + False + ..\..\..\..\..\..\..\AssembliesTPS\nunit.framework.dll + + + + + + + + + + + + + + + + + + + + + + + + + True + Application.myapp + + + True + True + Resources.resx + + + True + Settings.settings + True + + + + + VbMyResourcesResXFileCodeGenerator + Resources.Designer.vb + My.Resources + Designer + + + + + MyApplicationCodeGenerator + Application.Designer.vb + + + SettingsSingleFileGenerator + My + Settings.Designer.vb + + + + + + + + + \ No newline at end of file diff --git a/Moq.VisualBasicTests/My Project/Application.Designer.vb b/Moq.VisualBasicTests/My Project/Application.Designer.vb new file mode 100644 index 000000000..88dd01c78 --- /dev/null +++ b/Moq.VisualBasicTests/My Project/Application.Designer.vb @@ -0,0 +1,13 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:4.0.30319.42000 +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict On +Option Explicit On + diff --git a/Moq.VisualBasicTests/My Project/Application.myapp b/Moq.VisualBasicTests/My Project/Application.myapp new file mode 100644 index 000000000..758895def --- /dev/null +++ b/Moq.VisualBasicTests/My Project/Application.myapp @@ -0,0 +1,10 @@ + + + false + false + 0 + true + 0 + 1 + true + diff --git a/Moq.VisualBasicTests/My Project/AssemblyInfo.vb b/Moq.VisualBasicTests/My Project/AssemblyInfo.vb new file mode 100644 index 000000000..93fb40207 --- /dev/null +++ b/Moq.VisualBasicTests/My Project/AssemblyInfo.vb @@ -0,0 +1,35 @@ +Imports System +Imports System.Reflection +Imports System.Runtime.InteropServices + +' General Information about an assembly is controlled through the following +' set of attributes. Change these attribute values to modify the information +' associated with an assembly. + +' Review the values of the assembly attributes + + + + + + + + + + +'The following GUID is for the ID of the typelib if this project is exposed to COM + + +' Version information for an assembly consists of the following four values: +' +' Major Version +' Minor Version +' Build Number +' Revision +' +' You can specify all the values or you can default the Build and Revision Numbers +' by using the '*' as shown below: +' + + + diff --git a/Moq.VisualBasicTests/My Project/Resources.Designer.vb b/Moq.VisualBasicTests/My Project/Resources.Designer.vb new file mode 100644 index 000000000..66fb2066e --- /dev/null +++ b/Moq.VisualBasicTests/My Project/Resources.Designer.vb @@ -0,0 +1,63 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:4.0.30319.42000 +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict On +Option Explicit On + +Imports System + +Namespace My.Resources + + 'This class was auto-generated by the StronglyTypedResourceBuilder + 'class via a tool like ResGen or Visual Studio. + 'To add or remove a member, edit your .ResX file then rerun ResGen + 'with the /str option, or rebuild your VS project. + ''' + ''' A strongly-typed resource class, for looking up localized strings, etc. + ''' + _ + Friend Module Resources + + Private resourceMan As Global.System.Resources.ResourceManager + + Private resourceCulture As Global.System.Globalization.CultureInfo + + ''' + ''' Returns the cached ResourceManager instance used by this class. + ''' + _ + Friend ReadOnly Property ResourceManager() As Global.System.Resources.ResourceManager + Get + If Object.ReferenceEquals(resourceMan, Nothing) Then + Dim temp As Global.System.Resources.ResourceManager = New Global.System.Resources.ResourceManager("Moq.VisualBasicTests.Resources", GetType(Resources).Assembly) + resourceMan = temp + End If + Return resourceMan + End Get + End Property + + ''' + ''' Overrides the current thread's CurrentUICulture property for all + ''' resource lookups using this strongly typed resource class. + ''' + _ + Friend Property Culture() As Global.System.Globalization.CultureInfo + Get + Return resourceCulture + End Get + Set + resourceCulture = value + End Set + End Property + End Module +End Namespace diff --git a/Moq.VisualBasicTests/My Project/Resources.resx b/Moq.VisualBasicTests/My Project/Resources.resx new file mode 100644 index 000000000..af7dbebba --- /dev/null +++ b/Moq.VisualBasicTests/My Project/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Moq.VisualBasicTests/My Project/Settings.Designer.vb b/Moq.VisualBasicTests/My Project/Settings.Designer.vb new file mode 100644 index 000000000..ad991681d --- /dev/null +++ b/Moq.VisualBasicTests/My Project/Settings.Designer.vb @@ -0,0 +1,73 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:4.0.30319.42000 +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict On +Option Explicit On + + +Namespace My + + _ + Partial Friend NotInheritable Class MySettings + Inherits Global.System.Configuration.ApplicationSettingsBase + + Private Shared defaultInstance As MySettings = CType(Global.System.Configuration.ApplicationSettingsBase.Synchronized(New MySettings()),MySettings) + +#Region "My.Settings Auto-Save Functionality" +#If _MyType = "WindowsForms" Then + Private Shared addedHandler As Boolean + + Private Shared addedHandlerLockObject As New Object + + _ + Private Shared Sub AutoSaveSettings(ByVal sender As Global.System.Object, ByVal e As Global.System.EventArgs) + If My.Application.SaveMySettingsOnExit Then + My.Settings.Save() + End If + End Sub +#End If +#End Region + + Public Shared ReadOnly Property [Default]() As MySettings + Get + +#If _MyType = "WindowsForms" Then + If Not addedHandler Then + SyncLock addedHandlerLockObject + If Not addedHandler Then + AddHandler My.Application.Shutdown, AddressOf AutoSaveSettings + addedHandler = True + End If + End SyncLock + End If +#End If + Return defaultInstance + End Get + End Property + End Class +End Namespace + +Namespace My + + _ + Friend Module MySettingsProperty + + _ + Friend ReadOnly Property Settings() As Global.Moq.VisualBasicTests.My.MySettings + Get + Return Global.Moq.VisualBasicTests.My.MySettings.Default + End Get + End Property + End Module +End Namespace diff --git a/Moq.VisualBasicTests/My Project/Settings.settings b/Moq.VisualBasicTests/My Project/Settings.settings new file mode 100644 index 000000000..85b890b3c --- /dev/null +++ b/Moq.VisualBasicTests/My Project/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Moq.VisualBasicTests/project.json b/Moq.VisualBasicTests/project.json new file mode 100644 index 000000000..1f91f2a97 --- /dev/null +++ b/Moq.VisualBasicTests/project.json @@ -0,0 +1,13 @@ +{ + "dependencies": { + "Castle.Core": "3.3.3", + "xunit": "2.1.0", + "xunit.runner.visualstudio": "2.1.0" + }, + "frameworks": { + "net45": {} + }, + "runtimes": { + "win": {} + } +} \ No newline at end of file diff --git a/Moq.sln b/Moq.sln index 9217dfed3..f38be337b 100644 --- a/Moq.sln +++ b/Moq.sln @@ -17,6 +17,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Moq", "Source\Moq.csproj", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Moq.Tests", "UnitTests\Moq.Tests.csproj", "{81BBC911-4916-4E10-A955-752AE47CB2B9}" EndProject +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Moq.VisualBasicTests", "Moq.VisualBasicTests\Moq.VisualBasicTests.vbproj", "{95F2E2B2-2E06-4AC7-A86F-9B1B6369D4AD}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -37,6 +39,14 @@ Global {81BBC911-4916-4E10-A955-752AE47CB2B9}.Release|Any CPU.ActiveCfg = Release|Any CPU {81BBC911-4916-4E10-A955-752AE47CB2B9}.Release|Any CPU.Build.0 = Release|Any CPU {81BBC911-4916-4E10-A955-752AE47CB2B9}.Release|x86.ActiveCfg = Release|Any CPU + {95F2E2B2-2E06-4AC7-A86F-9B1B6369D4AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {95F2E2B2-2E06-4AC7-A86F-9B1B6369D4AD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {95F2E2B2-2E06-4AC7-A86F-9B1B6369D4AD}.Debug|x86.ActiveCfg = Debug|Any CPU + {95F2E2B2-2E06-4AC7-A86F-9B1B6369D4AD}.Debug|x86.Build.0 = Debug|Any CPU + {95F2E2B2-2E06-4AC7-A86F-9B1B6369D4AD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {95F2E2B2-2E06-4AC7-A86F-9B1B6369D4AD}.Release|Any CPU.Build.0 = Release|Any CPU + {95F2E2B2-2E06-4AC7-A86F-9B1B6369D4AD}.Release|x86.ActiveCfg = Release|Any CPU + {95F2E2B2-2E06-4AC7-A86F-9B1B6369D4AD}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Source/Interceptor.cs b/Source/Interceptor.cs index c662798bb..d5c8f460c 100644 --- a/Source/Interceptor.cs +++ b/Source/Interceptor.cs @@ -169,12 +169,17 @@ public override bool Equals(object obj) } var eq = key.fixedString == this.fixedString && key.values.Count == this.values.Count; + if(!eq) + { + return false; + } - var index = 0; - while (eq && index < this.values.Count) + for(int index=0; index < values.Count; index++) { - eq |= this.values[index] == key.values[index]; - index++; + if (this.values[index] != key.values[index]) + { + return false; + } } return eq; diff --git a/UnitTests/MockedEventsFixture.cs b/UnitTests/MockedEventsFixture.cs index 27fef72f0..80c42f69d 100644 --- a/UnitTests/MockedEventsFixture.cs +++ b/UnitTests/MockedEventsFixture.cs @@ -35,8 +35,7 @@ public void ShouldRaiseEventIfAttachedAfterUse() Assert.False(presenter.Canceled); - view.Raise(v => v.Canceled += null, EventArgs.Empty); - + view.Raise(v => v.Canceled += null, EventArgs.Empty); Assert.True(presenter.Canceled); } From 31b677e728d3a8172a5f5a775b84910483246684 Mon Sep 17 00:00:00 2001 From: Joe Enzminger Date: Thu, 20 Oct 2016 12:39:20 -0500 Subject: [PATCH 09/13] adds ReturnAsync extention method that accepts a Func --- Source/ReturnsExtensions.cs | 21 ++++++-- UnitTests/ReturnsExtensionsFixture.cs | 74 ++++++++++++++++++++++++++- 2 files changed, 90 insertions(+), 5 deletions(-) diff --git a/Source/ReturnsExtensions.cs b/Source/ReturnsExtensions.cs index fc6031e26..61fa8c3b0 100644 --- a/Source/ReturnsExtensions.cs +++ b/Source/ReturnsExtensions.cs @@ -22,10 +22,23 @@ public static IReturnsResult ReturnsAsync(this IReturns - /// Allows to specify the exception thrown by an asynchronous method. - /// - public static IReturnsResult ThrowsAsync(this IReturns> mock, Exception exception) where TMock : class + /// + /// Allows to specify the return value of an asynchronous method. + /// + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class + { + return mock.Returns(() => + { + var tcs = new TaskCompletionSource(); + tcs.SetResult(value()); + return tcs.Task; + }); + } + + /// + /// Allows to specify the exception thrown by an asynchronous method. + /// + public static IReturnsResult ThrowsAsync(this IReturns> mock, Exception exception) where TMock : class { var tcs = new TaskCompletionSource(); tcs.SetException(exception); diff --git a/UnitTests/ReturnsExtensionsFixture.cs b/UnitTests/ReturnsExtensionsFixture.cs index b65be9dc9..bf5663d19 100644 --- a/UnitTests/ReturnsExtensionsFixture.cs +++ b/UnitTests/ReturnsExtensionsFixture.cs @@ -93,7 +93,79 @@ public void ReturnsAsync_on_ValueParameterValueReturnType() Assert.Equal(37, task.Result); } - [Fact] + [Fact] + public void ReturnsAsyncFunc_on_NoParametersRefReturnType() + { + var mock = new Mock(); + mock.Setup(x => x.NoParametersRefReturnType()).ReturnsAsync(() => "TestString"); + + var task = mock.Object.NoParametersRefReturnType(); + + Assert.True(task.IsCompleted); + Assert.Equal("TestString", task.Result); + } + + [Fact] + public void ReturnsAsyncFunc_on_NoParametersValueReturnType() + { + var mock = new Mock(); + mock.Setup(x => x.NoParametersValueReturnType()).ReturnsAsync(() => 36); + + var task = mock.Object.NoParametersValueReturnType(); + + Assert.True(task.IsCompleted); + Assert.Equal(36, task.Result); + } + + [Fact] + public void ReturnsAsyncFunc_on_RefParameterRefReturnType() + { + var mock = new Mock(); + mock.Setup(x => x.RefParameterRefReturnType("Param1")).ReturnsAsync(() => "TestString"); + + var task = mock.Object.RefParameterRefReturnType("Param1"); + + Assert.True(task.IsCompleted); + Assert.Equal("TestString", task.Result); + } + + [Fact] + public void ReturnsAsyncFunc_on_RefParameterValueReturnType() + { + var mock = new Mock(); + mock.Setup(x => x.RefParameterValueReturnType("Param1")).ReturnsAsync(() => 36); + + var task = mock.Object.RefParameterValueReturnType("Param1"); + + Assert.True(task.IsCompleted); + Assert.Equal(36, task.Result); + } + + [Fact] + public void ReturnsAsyncFunc_on_ValueParameterRefReturnType() + { + var mock = new Mock(); + mock.Setup(x => x.ValueParameterRefReturnType(36)).ReturnsAsync(() => "TestString"); + + var task = mock.Object.ValueParameterRefReturnType(36); + + Assert.True(task.IsCompleted); + Assert.Equal("TestString", task.Result); + } + + [Fact] + public void ReturnsAsyncFunc_on_ValueParameterValueReturnType() + { + var mock = new Mock(); + mock.Setup(x => x.ValueParameterValueReturnType(36)).ReturnsAsync(() => 37); + + var task = mock.Object.ValueParameterValueReturnType(36); + + Assert.True(task.IsCompleted); + Assert.Equal(37, task.Result); + } + + [Fact] public void ThrowsAsync_on_NoParametersRefReturnType() { var mock = new Mock(); From cdd07e3290341b9be78f482391281d0b876e57b3 Mon Sep 17 00:00:00 2001 From: Joe Enzminger Date: Sat, 29 Oct 2016 12:27:18 -0500 Subject: [PATCH 10/13] updates ReturnsAsync<> extension methods to use .NET 4.5 Task.FromResult rather than create a TaskCompletionSource --- Source/ReturnsExtensions.cs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/Source/ReturnsExtensions.cs b/Source/ReturnsExtensions.cs index 61fa8c3b0..d5aec74ad 100644 --- a/Source/ReturnsExtensions.cs +++ b/Source/ReturnsExtensions.cs @@ -16,10 +16,7 @@ public static class ReturnsExtensions /// public static IReturnsResult ReturnsAsync(this IReturns> mock, TResult value) where TMock : class { - var tcs = new TaskCompletionSource(); - tcs.SetResult(value); - - return mock.Returns(tcs.Task); + return mock.Returns(Task.FromResult(value)); } /// @@ -27,12 +24,7 @@ public static IReturnsResult ReturnsAsync(this IReturns public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class { - return mock.Returns(() => - { - var tcs = new TaskCompletionSource(); - tcs.SetResult(value()); - return tcs.Task; - }); + return mock.ReturnsAsync(value()); } /// From 27d548d59a6fd3ce1ceef8d179f9763cb6105f76 Mon Sep 17 00:00:00 2001 From: Tomas Lycken Date: Fri, 4 Nov 2016 08:31:55 +0100 Subject: [PATCH 11/13] T4-generated overloads for ReturnsAsync(Func value) --- Source/Moq.csproj | 14 +++ Source/ReturnsExtensions.Generated.cs | 149 ++++++++++++++++++++++++++ Source/ReturnsExtensions.tt | 58 ++++++++++ 3 files changed, 221 insertions(+) create mode 100644 Source/ReturnsExtensions.Generated.cs create mode 100644 Source/ReturnsExtensions.tt diff --git a/Source/Moq.csproj b/Source/Moq.csproj index ef9032ee3..890e0c6c5 100644 --- a/Source/Moq.csproj +++ b/Source/Moq.csproj @@ -103,6 +103,11 @@ + + True + True + ReturnsExtensions.tt + @@ -228,6 +233,15 @@ Times.cs + + + TextTemplatingFileGenerator + ReturnsExtensions.Generated.cs + + + + + diff --git a/Source/ReturnsExtensions.Generated.cs b/Source/ReturnsExtensions.Generated.cs new file mode 100644 index 000000000..bdee9e645 --- /dev/null +++ b/Source/ReturnsExtensions.Generated.cs @@ -0,0 +1,149 @@ +#if !NET3x +using System; +using System.Threading.Tasks; +using Moq.Language; +using Moq.Language.Flow; + +namespace Moq +{ + /// + /// Defines async extension methods on IReturns. + /// + public static class GeneratedReturnsExtensions + { + /// + /// Allows to specify the return value of an asynchronous method. + /// + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class + { + return mock.Returns((T t) => Task.FromResult(value(t))); + } + + /// + /// Specifies a function that will calculate the value to return from the method, + /// retrieving the arguments for the invocation. + /// + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class + { + return mock.Returns((T1 t1, T2 t2) => Task.FromResult(value(t1, t2))); + } + + /// + /// Specifies a function that will calculate the value to return from the method, + /// retrieving the arguments for the invocation. + /// + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class + { + return mock.Returns((T1 t1, T2 t2, T3 t3) => Task.FromResult(value(t1, t2, t3))); + } + + /// + /// Specifies a function that will calculate the value to return from the method, + /// retrieving the arguments for the invocation. + /// + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class + { + return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4) => Task.FromResult(value(t1, t2, t3, t4))); + } + + /// + /// Specifies a function that will calculate the value to return from the method, + /// retrieving the arguments for the invocation. + /// + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class + { + return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4, T5 t5) => Task.FromResult(value(t1, t2, t3, t4, t5))); + } + + /// + /// Specifies a function that will calculate the value to return from the method, + /// retrieving the arguments for the invocation. + /// + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class + { + return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6) => Task.FromResult(value(t1, t2, t3, t4, t5, t6))); + } + + /// + /// Specifies a function that will calculate the value to return from the method, + /// retrieving the arguments for the invocation. + /// + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class + { + return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7) => Task.FromResult(value(t1, t2, t3, t4, t5, t6, t7))); + } + + /// + /// Specifies a function that will calculate the value to return from the method, + /// retrieving the arguments for the invocation. + /// + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class + { + return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8) => Task.FromResult(value(t1, t2, t3, t4, t5, t6, t7, t8))); + } + + /// + /// Specifies a function that will calculate the value to return from the method, + /// retrieving the arguments for the invocation. + /// + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class + { + return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9) => Task.FromResult(value(t1, t2, t3, t4, t5, t6, t7, t8, t9))); + } + + /// + /// Specifies a function that will calculate the value to return from the method, + /// retrieving the arguments for the invocation. + /// + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class + { + return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10) => Task.FromResult(value(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10))); + } + + /// + /// Specifies a function that will calculate the value to return from the method, + /// retrieving the arguments for the invocation. + /// + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class + { + return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10, T11 t11) => Task.FromResult(value(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11))); + } + + /// + /// Specifies a function that will calculate the value to return from the method, + /// retrieving the arguments for the invocation. + /// + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class + { + return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10, T11 t11, T12 t12) => Task.FromResult(value(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12))); + } + + /// + /// Specifies a function that will calculate the value to return from the method, + /// retrieving the arguments for the invocation. + /// + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class + { + return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10, T11 t11, T12 t12, T13 t13) => Task.FromResult(value(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13))); + } + + /// + /// Specifies a function that will calculate the value to return from the method, + /// retrieving the arguments for the invocation. + /// + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class + { + return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10, T11 t11, T12 t12, T13 t13, T14 t14) => Task.FromResult(value(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14))); + } + + /// + /// Specifies a function that will calculate the value to return from the method, + /// retrieving the arguments for the invocation. + /// + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class + { + return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10, T11 t11, T12 t12, T13 t13, T14 t14, T15 t15) => Task.FromResult(value(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15))); + } + } +} +#endif \ No newline at end of file diff --git a/Source/ReturnsExtensions.tt b/Source/ReturnsExtensions.tt new file mode 100644 index 000000000..da2924955 --- /dev/null +++ b/Source/ReturnsExtensions.tt @@ -0,0 +1,58 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ output extension=".Generated.cs" #> +<#@ Assembly Name="System.Core" #> +<#@ Import Namespace="System.Linq" #> +#if !NET3x +using System; +using System.Threading.Tasks; +using Moq.Language; +using Moq.Language.Flow; + +namespace Moq +{ + /// + /// Defines async extension methods on IReturns. + /// + public static class GeneratedReturnsExtensions + { + /// + /// Allows to specify the return value of an asynchronous method. + /// + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class + { + return mock.Returns((T t) => Task.FromResult(value(t))); + } + <# + for (var argumentCount = 2; argumentCount <= 15; ++argumentCount) + { #> + /// + /// Specifies a function that will calculate the value to return from the method, + /// retrieving the arguments for the invocation. + /// + public static IReturnsResult ReturnsAsync<<# + for (var i = 1; i <= argumentCount; ++i) + { + #>T<#=i#>, <# + }#>TMock, TResult>(this IReturns> mock, Func<<# + for (var i = 1; i <= argumentCount; ++i) + { + #>T<#=i#>, <# + }#>TResult> value) where TMock : class + { + return mock.Returns((<# + for (var i = 1; i <= argumentCount; ++i) + { #>T<#=i#> t<#=i#><# + if(i != argumentCount) {#>, <#}#><# + }#>) => Task.FromResult(value(<# + for (var i = 1; i <= argumentCount; ++i) + { + #>t<#=i#><# + if (i != argumentCount) { #>, <#}#><# + }#>))); + } +<# + } + #> + } +} +#endif \ No newline at end of file From 9bc3b834ca6586d744f6119a8645a70c07151ea7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Saro=20Ta=C5=9Fciyan?= Date: Sat, 10 Dec 2016 19:35:05 +0200 Subject: [PATCH 12/13] ReturnsAsync() lazy evaluation issue fix #303 (#309) * ReturnsAsync() lazy evaluation issue fix #303 --- Source/ReturnsExtensions.Generated.cs | 197 ++++++++++-------- Source/ReturnsExtensions.cs | 34 ++- Source/ReturnsExtensions.tt | 28 ++- .../GeneratedReturnsExtensionsFixture.cs | 95 +++++++++ UnitTests/Moq.Tests.csproj | 1 + UnitTests/ReturnsExtensionsFixture.cs | 28 ++- 6 files changed, 271 insertions(+), 112 deletions(-) create mode 100644 UnitTests/GeneratedReturnsExtensionsFixture.cs diff --git a/Source/ReturnsExtensions.Generated.cs b/Source/ReturnsExtensions.Generated.cs index bdee9e645..041a46494 100644 --- a/Source/ReturnsExtensions.Generated.cs +++ b/Source/ReturnsExtensions.Generated.cs @@ -11,138 +11,157 @@ namespace Moq /// public static class GeneratedReturnsExtensions { - /// - /// Allows to specify the return value of an asynchronous method. - /// - public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class + /// + /// Specifies a function that will calculate the value to return from the asynchronous method. + /// + /// Type of the function parameter. + /// Mocked type. + /// Type of the return value. + /// Returns verb which represents the mocked type and the task of return type + /// The function that will calculate the return value. + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func valueFunction) where TMock : class { - return mock.Returns((T t) => Task.FromResult(value(t))); + return mock.Returns((T t) => Task.FromResult(valueFunction(t))); } - /// - /// Specifies a function that will calculate the value to return from the method, - /// retrieving the arguments for the invocation. - /// - public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class + /// + /// Specifies a function that will calculate the value to return from the asynchronous method. + /// + /// Returns verb which represents the mocked type and the task of return type + /// The function that will calculate the return value. + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func valueFunction) where TMock : class { - return mock.Returns((T1 t1, T2 t2) => Task.FromResult(value(t1, t2))); + return mock.Returns((T1 t1, T2 t2) => Task.FromResult(valueFunction(t1, t2))); } - /// - /// Specifies a function that will calculate the value to return from the method, - /// retrieving the arguments for the invocation. - /// - public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class + /// + /// Specifies a function that will calculate the value to return from the asynchronous method. + /// + /// Returns verb which represents the mocked type and the task of return type + /// The function that will calculate the return value. + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func valueFunction) where TMock : class { - return mock.Returns((T1 t1, T2 t2, T3 t3) => Task.FromResult(value(t1, t2, t3))); + return mock.Returns((T1 t1, T2 t2, T3 t3) => Task.FromResult(valueFunction(t1, t2, t3))); } - /// - /// Specifies a function that will calculate the value to return from the method, - /// retrieving the arguments for the invocation. - /// - public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class + /// + /// Specifies a function that will calculate the value to return from the asynchronous method. + /// + /// Returns verb which represents the mocked type and the task of return type + /// The function that will calculate the return value. + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func valueFunction) where TMock : class { - return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4) => Task.FromResult(value(t1, t2, t3, t4))); + return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4) => Task.FromResult(valueFunction(t1, t2, t3, t4))); } - /// - /// Specifies a function that will calculate the value to return from the method, - /// retrieving the arguments for the invocation. - /// - public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class + /// + /// Specifies a function that will calculate the value to return from the asynchronous method. + /// + /// Returns verb which represents the mocked type and the task of return type + /// The function that will calculate the return value. + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func valueFunction) where TMock : class { - return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4, T5 t5) => Task.FromResult(value(t1, t2, t3, t4, t5))); + return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4, T5 t5) => Task.FromResult(valueFunction(t1, t2, t3, t4, t5))); } - /// - /// Specifies a function that will calculate the value to return from the method, - /// retrieving the arguments for the invocation. - /// - public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class + /// + /// Specifies a function that will calculate the value to return from the asynchronous method. + /// + /// Returns verb which represents the mocked type and the task of return type + /// The function that will calculate the return value. + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func valueFunction) where TMock : class { - return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6) => Task.FromResult(value(t1, t2, t3, t4, t5, t6))); + return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6) => Task.FromResult(valueFunction(t1, t2, t3, t4, t5, t6))); } - /// - /// Specifies a function that will calculate the value to return from the method, - /// retrieving the arguments for the invocation. - /// - public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class + /// + /// Specifies a function that will calculate the value to return from the asynchronous method. + /// + /// Returns verb which represents the mocked type and the task of return type + /// The function that will calculate the return value. + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func valueFunction) where TMock : class { - return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7) => Task.FromResult(value(t1, t2, t3, t4, t5, t6, t7))); + return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7) => Task.FromResult(valueFunction(t1, t2, t3, t4, t5, t6, t7))); } - /// - /// Specifies a function that will calculate the value to return from the method, - /// retrieving the arguments for the invocation. - /// - public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class + /// + /// Specifies a function that will calculate the value to return from the asynchronous method. + /// + /// Returns verb which represents the mocked type and the task of return type + /// The function that will calculate the return value. + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func valueFunction) where TMock : class { - return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8) => Task.FromResult(value(t1, t2, t3, t4, t5, t6, t7, t8))); + return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8) => Task.FromResult(valueFunction(t1, t2, t3, t4, t5, t6, t7, t8))); } - /// - /// Specifies a function that will calculate the value to return from the method, - /// retrieving the arguments for the invocation. - /// - public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class + /// + /// Specifies a function that will calculate the value to return from the asynchronous method. + /// + /// Returns verb which represents the mocked type and the task of return type + /// The function that will calculate the return value. + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func valueFunction) where TMock : class { - return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9) => Task.FromResult(value(t1, t2, t3, t4, t5, t6, t7, t8, t9))); + return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9) => Task.FromResult(valueFunction(t1, t2, t3, t4, t5, t6, t7, t8, t9))); } - /// - /// Specifies a function that will calculate the value to return from the method, - /// retrieving the arguments for the invocation. - /// - public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class + /// + /// Specifies a function that will calculate the value to return from the asynchronous method. + /// + /// Returns verb which represents the mocked type and the task of return type + /// The function that will calculate the return value. + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func valueFunction) where TMock : class { - return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10) => Task.FromResult(value(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10))); + return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10) => Task.FromResult(valueFunction(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10))); } - /// - /// Specifies a function that will calculate the value to return from the method, - /// retrieving the arguments for the invocation. - /// - public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class + /// + /// Specifies a function that will calculate the value to return from the asynchronous method. + /// + /// Returns verb which represents the mocked type and the task of return type + /// The function that will calculate the return value. + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func valueFunction) where TMock : class { - return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10, T11 t11) => Task.FromResult(value(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11))); + return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10, T11 t11) => Task.FromResult(valueFunction(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11))); } - /// - /// Specifies a function that will calculate the value to return from the method, - /// retrieving the arguments for the invocation. - /// - public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class + /// + /// Specifies a function that will calculate the value to return from the asynchronous method. + /// + /// Returns verb which represents the mocked type and the task of return type + /// The function that will calculate the return value. + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func valueFunction) where TMock : class { - return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10, T11 t11, T12 t12) => Task.FromResult(value(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12))); + return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10, T11 t11, T12 t12) => Task.FromResult(valueFunction(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12))); } - /// - /// Specifies a function that will calculate the value to return from the method, - /// retrieving the arguments for the invocation. - /// - public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class + /// + /// Specifies a function that will calculate the value to return from the asynchronous method. + /// + /// Returns verb which represents the mocked type and the task of return type + /// The function that will calculate the return value. + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func valueFunction) where TMock : class { - return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10, T11 t11, T12 t12, T13 t13) => Task.FromResult(value(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13))); + return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10, T11 t11, T12 t12, T13 t13) => Task.FromResult(valueFunction(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13))); } - /// - /// Specifies a function that will calculate the value to return from the method, - /// retrieving the arguments for the invocation. - /// - public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class + /// + /// Specifies a function that will calculate the value to return from the asynchronous method. + /// + /// Returns verb which represents the mocked type and the task of return type + /// The function that will calculate the return value. + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func valueFunction) where TMock : class { - return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10, T11 t11, T12 t12, T13 t13, T14 t14) => Task.FromResult(value(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14))); + return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10, T11 t11, T12 t12, T13 t13, T14 t14) => Task.FromResult(valueFunction(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14))); } - /// - /// Specifies a function that will calculate the value to return from the method, - /// retrieving the arguments for the invocation. - /// - public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class + /// + /// Specifies a function that will calculate the value to return from the asynchronous method. + /// + /// Returns verb which represents the mocked type and the task of return type + /// The function that will calculate the return value. + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func valueFunction) where TMock : class { - return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10, T11 t11, T12 t12, T13 t13, T14 t14, T15 t15) => Task.FromResult(value(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15))); + return mock.Returns((T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10, T11 t11, T12 t12, T13 t13, T14 t14, T15 t15) => Task.FromResult(valueFunction(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15))); } } } diff --git a/Source/ReturnsExtensions.cs b/Source/ReturnsExtensions.cs index d5aec74ad..eb9eba1e8 100644 --- a/Source/ReturnsExtensions.cs +++ b/Source/ReturnsExtensions.cs @@ -12,25 +12,37 @@ namespace Moq public static class ReturnsExtensions { /// - /// Allows to specify the return value of an asynchronous method. + /// Specifies the value to return from an asynchronous method. /// + /// Mocked type. + /// Type of the return value. + /// Returns verb which represents the mocked type and the task of return type + /// The value to return, or . public static IReturnsResult ReturnsAsync(this IReturns> mock, TResult value) where TMock : class { - return mock.Returns(Task.FromResult(value)); + return mock.ReturnsAsync(() => value); } - /// - /// Allows to specify the return value of an asynchronous method. - /// - public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class + /// + /// Specifies a function that will calculate the value to return from the asynchronous method. + /// + /// Mocked type. + /// Type of the return value. + /// Returns verb which represents the mocked type and the task of return type + /// The function that will calculate the return value. + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func valueFunction) where TMock : class { - return mock.ReturnsAsync(value()); + return mock.Returns(() => Task.FromResult(valueFunction())); } - /// - /// Allows to specify the exception thrown by an asynchronous method. - /// - public static IReturnsResult ThrowsAsync(this IReturns> mock, Exception exception) where TMock : class + /// + /// Specifies the exception to throw when the asynchronous method is invoked. + /// + /// Mocked type. + /// Type of the return value. + /// Returns verb which represents the mocked type and the task of return type + /// Exception instance to throw. + public static IReturnsResult ThrowsAsync(this IReturns> mock, Exception exception) where TMock : class { var tcs = new TaskCompletionSource(); tcs.SetException(exception); diff --git a/Source/ReturnsExtensions.tt b/Source/ReturnsExtensions.tt index da2924955..a3d7b914f 100644 --- a/Source/ReturnsExtensions.tt +++ b/Source/ReturnsExtensions.tt @@ -15,20 +15,26 @@ namespace Moq /// public static class GeneratedReturnsExtensions { - /// - /// Allows to specify the return value of an asynchronous method. - /// - public static IReturnsResult ReturnsAsync(this IReturns> mock, Func value) where TMock : class + /// + /// Specifies a function that will calculate the value to return from the asynchronous method. + /// + /// Type of the function parameter. + /// Mocked type. + /// Type of the return value. + /// Returns verb which represents the mocked type and the task of return type + /// The function that will calculate the return value. + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func valueFunction) where TMock : class { - return mock.Returns((T t) => Task.FromResult(value(t))); + return mock.Returns((T t) => Task.FromResult(valueFunction(t))); } <# for (var argumentCount = 2; argumentCount <= 15; ++argumentCount) { #> - /// - /// Specifies a function that will calculate the value to return from the method, - /// retrieving the arguments for the invocation. - /// + /// + /// Specifies a function that will calculate the value to return from the asynchronous method. + /// + /// Returns verb which represents the mocked type and the task of return type + /// The function that will calculate the return value. public static IReturnsResult ReturnsAsync<<# for (var i = 1; i <= argumentCount; ++i) { @@ -37,13 +43,13 @@ namespace Moq for (var i = 1; i <= argumentCount; ++i) { #>T<#=i#>, <# - }#>TResult> value) where TMock : class + }#>TResult> valueFunction) where TMock : class { return mock.Returns((<# for (var i = 1; i <= argumentCount; ++i) { #>T<#=i#> t<#=i#><# if(i != argumentCount) {#>, <#}#><# - }#>) => Task.FromResult(value(<# + }#>) => Task.FromResult(valueFunction(<# for (var i = 1; i <= argumentCount; ++i) { #>t<#=i#><# diff --git a/UnitTests/GeneratedReturnsExtensionsFixture.cs b/UnitTests/GeneratedReturnsExtensionsFixture.cs new file mode 100644 index 000000000..24cb4ee0c --- /dev/null +++ b/UnitTests/GeneratedReturnsExtensionsFixture.cs @@ -0,0 +1,95 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +namespace Moq.Tests +{ + public class GeneratedReturnsExtensionsFixture + { + public interface IAsyncInterface + { + Task WithSingleParameterAsync(int parameter); + Task WithMultiParameterAsync(string firstParameter, string secondParameter); + Task WithParamsAsync(params DateTime[] dateTimes); + } + + [Fact] + public void ReturnsAsync_onSingleParameter_ParameterUsedForCalculationOfTheResult() + { + var mock = new Mock(); + mock.Setup(x => x.WithSingleParameterAsync(It.IsAny())).ReturnsAsync((int x) => x * x); + + int evaluationResult = mock.Object.WithSingleParameterAsync(2).Result; + + Assert.Equal(4, evaluationResult); + } + + [Fact] + public void ReturnsAsync_onSingleParameter_LazyEvaluationOfTheResult() + { + int coefficient = 5; + var mock = new Mock(); + mock.Setup(x => x.WithSingleParameterAsync(It.IsAny())).ReturnsAsync((int x) => x * coefficient); + + int firstEvaluationResult = mock.Object.WithSingleParameterAsync(2).Result; + + coefficient = 10; + int secondEvaluationResult = mock.Object.WithSingleParameterAsync(2).Result; + + Assert.NotEqual(firstEvaluationResult, secondEvaluationResult); + } + + [Fact] + public void ReturnsAsync_onMultiParameter_ParametersUsedForCalculationOfTheResult() + { + var mock = new Mock(); + mock.Setup(x => x.WithMultiParameterAsync(It.IsAny(), It.IsAny())).ReturnsAsync((string first, string second) => first + second); + + string evaluationResult = mock.Object.WithMultiParameterAsync("Moq", "4").Result; + + Assert.Equal("Moq4", evaluationResult); + } + + [Fact] + public void ReturnsAsync_onMultiParameter_LazyEvaluationOfTheResult() + { + var mock = new Mock(); + mock.Setup(x => x.WithMultiParameterAsync(It.IsAny(), It.IsAny())).ReturnsAsync((string first, string second) => first + second); + + string firstEvaluationResult = mock.Object.WithMultiParameterAsync("Moq", "4").Result; + string secondEvaluationResult = mock.Object.WithMultiParameterAsync("Moq", "4").Result; + + Assert.NotSame(firstEvaluationResult, secondEvaluationResult); + } + + [Fact] + public void ReturnsAsync_onParams_AllParametersUsedForCalculationOfTheResult() + { + var mock = new Mock(); + mock.Setup(x => x.WithParamsAsync(It.IsAny())) + .ReturnsAsync((DateTime[] dateTimes) => dateTimes.Max()); + + DateTime evaluationResult = mock.Object.WithParamsAsync(DateTime.MinValue, DateTime.Now, DateTime.MaxValue).Result; + + Assert.Equal(DateTime.MaxValue, evaluationResult); + } + + [Fact] + public void ReturnsAsync_onParams_LazyEvaluationOfTheResult() + { + DateTime comparedDateTime = DateTime.MinValue; + var mock = new Mock(); + mock.Setup(x => x.WithParamsAsync(It.IsAny())) + .ReturnsAsync((DateTime[] dateTimes) => dateTimes.Concat(new[] { comparedDateTime }).Max()); + + DateTime now = DateTime.Now; + DateTime firstEvaluationResult = mock.Object.WithParamsAsync(DateTime.MinValue, now).Result; + + comparedDateTime = DateTime.MaxValue; + DateTime secondEvaluationResult = mock.Object.WithParamsAsync(DateTime.MinValue, now).Result; + + Assert.NotEqual(firstEvaluationResult, secondEvaluationResult); + } + } +} \ No newline at end of file diff --git a/UnitTests/Moq.Tests.csproj b/UnitTests/Moq.Tests.csproj index 1aed1d809..4f48b6cef 100644 --- a/UnitTests/Moq.Tests.csproj +++ b/UnitTests/Moq.Tests.csproj @@ -60,6 +60,7 @@ + diff --git a/UnitTests/ReturnsExtensionsFixture.cs b/UnitTests/ReturnsExtensionsFixture.cs index bf5663d19..0ed2d211e 100644 --- a/UnitTests/ReturnsExtensionsFixture.cs +++ b/UnitTests/ReturnsExtensionsFixture.cs @@ -19,6 +19,8 @@ public interface IAsyncInterface Task ValueParameterRefReturnType(int value); Task ValueParameterValueReturnType(int value); + + Task NewGuidAsync(); } [Fact] @@ -165,7 +167,31 @@ public void ReturnsAsyncFunc_on_ValueParameterValueReturnType() Assert.Equal(37, task.Result); } - [Fact] + [Fact] + public void ReturnsAsyncFunc_onEachInvocation_ValueReturnTypeLazyEvaluation() + { + var mock = new Mock(); + mock.Setup(x => x.NewGuidAsync()).ReturnsAsync(Guid.NewGuid); + + Guid firstEvaluationResult = mock.Object.NewGuidAsync().Result; + Guid secondEvaluationResult = mock.Object.NewGuidAsync().Result; + + Assert.NotEqual(firstEvaluationResult, secondEvaluationResult); + } + + [Fact] + public void ReturnsAsyncFunc_onEachInvocation_RefReturnTypeLazyEvaluation() + { + var mock = new Mock(); + mock.Setup(x => x.ValueParameterRefReturnType(36)).ReturnsAsync(() => new string(new[] { 'M', 'o', 'q', '4' })); + + string firstEvaluationResult = mock.Object.ValueParameterRefReturnType(36).Result; + string secondEvaluationResult = mock.Object.ValueParameterRefReturnType(36).Result; + + Assert.NotSame(firstEvaluationResult, secondEvaluationResult); + } + + [Fact] public void ThrowsAsync_on_NoParametersRefReturnType() { var mock = new Mock(); From 195219e923e744922212e86301ecfb624635a451 Mon Sep 17 00:00:00 2001 From: Matthew Kubicki Date: Mon, 9 Jan 2017 04:03:02 +0000 Subject: [PATCH 13/13] Fixing #295 and changing fix for #278 (#313) * Fixes #278. Only fixes some of the issue though. Also adds new VB test assembly, hopefully in a way people will be happy with * Another fix for issue #278 that no longer causes issue #295. There is still problematic code in the Interceptor for comparing the arguments of calls, but for now it no longer causes a problem with propeties with arguments as the expression string builder now reports the arguments for property reads. I've added a comment explaining the flaws (as I see them) in the Interceptor code in the hope that it will help when someone next as a problem, or at least stops them missing the same thing as I did earlier. --- Source/ExpressionStringBuilder.cs | 12 ++++++++---- Source/Interceptor.cs | 22 +++++++++++----------- UnitTests/MatchersFixture.cs | 18 ++++++++++++++++-- 3 files changed, 35 insertions(+), 17 deletions(-) diff --git a/Source/ExpressionStringBuilder.cs b/Source/ExpressionStringBuilder.cs index 288f50d2b..512768f70 100644 --- a/Source/ExpressionStringBuilder.cs +++ b/Source/ExpressionStringBuilder.cs @@ -343,15 +343,19 @@ private void ToStringMethodCall(MethodCallExpression node) else if (node.Method.IsPropertyIndexerSetter()) { this.builder.Append("["); - AsCommaSeparatedValues(node.Arguments - .Skip(paramFrom) - .Take(node.Arguments.Count - paramFrom), ToString); + AsCommaSeparatedValues(node.Arguments.Skip(paramFrom), ToString); this.builder.Append("] = "); ToString(node.Arguments.Last()); } else if (node.Method.IsPropertyGetter()) { - this.builder.Append(".").Append(node.Method.Name.Substring(4)); + this.builder.Append(".").Append(node.Method.Name.Substring(4)); + if (node.Arguments.Count > paramFrom) + { + this.builder.Append("["); + AsCommaSeparatedValues(node.Arguments.Skip(paramFrom), ToString); + this.builder.Append("]"); + } } else if (node.Method.IsPropertySetter()) { diff --git a/Source/Interceptor.cs b/Source/Interceptor.cs index d5c8f460c..b007110f4 100644 --- a/Source/Interceptor.cs +++ b/Source/Interceptor.cs @@ -169,20 +169,20 @@ public override bool Equals(object obj) } var eq = key.fixedString == this.fixedString && key.values.Count == this.values.Count; - if(!eq) + + //the code below is broken as it uses an OR when checking the arguments, this means that if any pair of arguments match + //the result is a match. + //This is only going to hit some edge cases as for the most part the fixed string above spots arguments correctly. + //Fixing this really needs a reworking of the GetHashCode, and also some sorting out of how to compare value types that have been + //boxed correctly (another problem this code has) + var index = 0; + while (eq && index < this.values.Count) { - return false; + eq |= this.values[index] == key.values[index]; + index++; } - for(int index=0; index < values.Count; index++) - { - if (this.values[index] != key.values[index]) - { - return false; - } - } - - return eq; + return eq; } public override int GetHashCode() diff --git a/UnitTests/MatchersFixture.cs b/UnitTests/MatchersFixture.cs index 7b32e6476..dacbe869c 100644 --- a/UnitTests/MatchersFixture.cs +++ b/UnitTests/MatchersFixture.cs @@ -266,7 +266,20 @@ public void MatchingNonNullableValueTypeForNullableParameterDoesNotMatchNull() Assert.Equal(0, mock.Object.TakesNullableParameter(null)); } - private int GetToRange() + [Fact] + public void MultipleMatchingSetupsWithMultiplValueTypeArgumentsReplaceEachOtherForVerify() + { + var mock = new Mock(); + + mock.Setup(x => x.TakesTwoValueTypes(1, 2)).Verifiable(); + mock.Setup(x => x.TakesTwoValueTypes(1, 2)).Verifiable(); + + mock.Object.TakesTwoValueTypes(1, 2); + + mock.Verify(); + } + + private int GetToRange() { return 5; } @@ -283,6 +296,7 @@ public interface IFoo int DoAddition(int[] numbers); int[] Items { get; set; } int TakesNullableParameter(int? value); - } + void TakesTwoValueTypes(int a, int b); + } } }