diff --git a/.gitignore b/.gitignore index 0cdcb19c..945207e7 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ obj *.log *.binlog *.rsp +/src/Usage diff --git a/src/Moq/Moq.Analyzer/Processors/CSharpMocked.cs b/src/Moq/Moq.Analyzer/Processors/CSharpMocked.cs index cc1c9962..915eff37 100644 --- a/src/Moq/Moq.Analyzer/Processors/CSharpMocked.cs +++ b/src/Moq/Moq.Analyzer/Processors/CSharpMocked.cs @@ -86,7 +86,7 @@ public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node) Argument(RefExpression(IdentifierName("mock"))), Argument(ParenthesizedLambdaExpression( ObjectCreationExpression( - IdentifierName(nameof(MockInfo))) + IdentifierName(nameof(DefaultMock))) .WithArgumentList(ArgumentList(SingletonSeparatedList(Argument( ThisExpression() )))) diff --git a/src/Moq/Moq.Analyzer/Processors/VisualBasicMocked.cs b/src/Moq/Moq.Analyzer/Processors/VisualBasicMocked.cs index 66356417..1af57c44 100644 --- a/src/Moq/Moq.Analyzer/Processors/VisualBasicMocked.cs +++ b/src/Moq/Moq.Analyzer/Processors/VisualBasicMocked.cs @@ -83,7 +83,7 @@ public override SyntaxNode VisitClassBlock(ClassBlockSyntax node) FunctionLambdaHeader(List(), TokenList(), ParameterList(), null), ObjectCreationExpression( List(), - IdentifierName(nameof(MockInfo)), + IdentifierName(nameof(DefaultMock)), ArgumentList(SingletonSeparatedList( SimpleArgument(MeExpression()) )), diff --git a/src/Moq/Moq.Sdk.Tests/MockExtensionsTests.cs b/src/Moq/Moq.Sdk.Tests/MockExtensionsTests.cs index 57342d28..14cb6bc3 100644 --- a/src/Moq/Moq.Sdk.Tests/MockExtensionsTests.cs +++ b/src/Moq/Moq.Sdk.Tests/MockExtensionsTests.cs @@ -118,7 +118,7 @@ class TestMockBehavior : IMockBehavior { public IMockSetup Setup { get; set; } = new MockSetup(new FakeInvocation(), new IArgumentMatcher[0]); - public ObservableCollection Behaviors => null; + public ObservableCollection Behaviors => null; public bool AppliesTo(IMethodInvocation invocation) => false; @@ -131,6 +131,8 @@ class Mock : IMock public IList Invocations { get; } = new List(); + public object Object { get; set; } + public MockState State => new MockState(); public IMockSetup LastSetup { get; set; } diff --git a/src/Moq/Moq.Sdk.Tests/Mocked.cs b/src/Moq/Moq.Sdk.Tests/Mocked.cs index a0483d0c..4c85adc6 100644 --- a/src/Moq/Moq.Sdk.Tests/Mocked.cs +++ b/src/Moq/Moq.Sdk.Tests/Mocked.cs @@ -10,7 +10,7 @@ public class Mocked : IMocked, IStunt IMock mock; ObservableCollection behaviors = new ObservableCollection(); - public IMock Mock => LazyInitializer.EnsureInitialized(ref mock, () => new MockInfo(this)); + public IMock Mock => LazyInitializer.EnsureInitialized(ref mock, () => new DefaultMock(this)); public ObservableCollection Behaviors => behaviors; } diff --git a/src/Moq/Moq.Sdk/Behavior.cs b/src/Moq/Moq.Sdk/Behavior.cs new file mode 100644 index 00000000..dc1584b1 --- /dev/null +++ b/src/Moq/Moq.Sdk/Behavior.cs @@ -0,0 +1,56 @@ +using System; +using System.Diagnostics; +using Stunts; + +namespace Moq.Sdk +{ + /// + /// Represents an invokable behavior in the + /// that applies whenever the is matched in a + /// mock runtime call. + /// + [DebuggerDisplay("{DisplayName}")] + public class Behavior : IBehavior + { + Lazy displayName; + + /// + /// Creates an instance of the invokable behavior with the given + /// delegate and friendly display name. + /// + public Behavior(InvokeBehavior invoke, string displayName) + : this(invoke, new Lazy(() => displayName)) + { + } + + /// + /// Creates an instance of the invokable behavior with the given + /// delegate and friendly display name. + /// + /// + /// Use this constructor overload whenver constructing the display + /// name is somewhat expensive. + /// + public Behavior(InvokeBehavior invoke, Lazy displayName) + { + Invoke = invoke; + this.displayName = displayName; + } + + /// + /// The delegate that implements the actual behavior. + /// + public InvokeBehavior Invoke { get; } + + /// + /// A friendly display name that describes what invoking the + /// delegate will do. + /// + public string DisplayName => displayName.Value; + + /// + /// Returns the . + /// + public override string ToString() => DisplayName; + } +} \ No newline at end of file diff --git a/src/Moq/Moq.Sdk/MockInfo.cs b/src/Moq/Moq.Sdk/DefaultMock.cs similarity index 85% rename from src/Moq/Moq.Sdk/MockInfo.cs rename to src/Moq/Moq.Sdk/DefaultMock.cs index c90f0d52..a7264d68 100644 --- a/src/Moq/Moq.Sdk/MockInfo.cs +++ b/src/Moq/Moq.Sdk/DefaultMock.cs @@ -11,12 +11,12 @@ namespace Moq.Sdk /// /// Default implementation of the mock introspection API /// - public class MockInfo : IMock + public class DefaultMock : IMock { - IStunt stunt; - ConcurrentDictionary setupBehaviorMap = new ConcurrentDictionary(); + readonly IStunt stunt; + readonly ConcurrentDictionary setupBehaviorMap = new ConcurrentDictionary(); - public MockInfo(IStunt stunt) + public DefaultMock(IStunt stunt) { this.stunt = stunt ?? throw new ArgumentNullException(nameof(stunt)); stunt.Behaviors.CollectionChanged += OnBehaviorsChanged; @@ -28,15 +28,19 @@ public MockInfo(IStunt stunt) /// public IList Invocations { get; } = new List(); + /// + public object Object => stunt; + /// public MockState State { get; } = new MockState(); public IMockBehavior BehaviorFor(IMockSetup setup) => setupBehaviorMap.GetOrAdd(setup, x => { - var behavior = new MockBehavior(x); + var behavior = new DefaultMockBehavior(x); // The tracking behavior must appear before the mock behaviors. var tracking = Behaviors.OfType().FirstOrDefault(); + // NOTE: latest setup wins, since it goes to the top of the list. var index = tracking == null ? 0 : (Behaviors.IndexOf(tracking) + 1); Behaviors.Insert(index, behavior); return behavior; @@ -65,8 +69,6 @@ void OnBehaviorsChanged(object sender, NotifyCollectionChangedEventArgs e) foreach (var behavior in Behaviors.OfType()) setupBehaviorMap.TryAdd(behavior.Setup, behavior); break; - default: - break; } } } diff --git a/src/Moq/Moq.Sdk/MockBehavior.cs b/src/Moq/Moq.Sdk/DefaultMockBehavior.cs similarity index 66% rename from src/Moq/Moq.Sdk/MockBehavior.cs rename to src/Moq/Moq.Sdk/DefaultMockBehavior.cs index 097c9650..2a02e601 100644 --- a/src/Moq/Moq.Sdk/MockBehavior.cs +++ b/src/Moq/Moq.Sdk/DefaultMockBehavior.cs @@ -5,12 +5,21 @@ namespace Moq.Sdk { - /// - public class MockBehavior : IMockBehavior + /// + /// Default implementation of , which implements + /// effectively a sub-pipeline of behaviors within the 's + /// , using the + /// abstraction over the member + /// to determine which ones to apply. + /// + /// + /// + /// + public class DefaultMockBehavior : IMockBehavior { - public MockBehavior(IMockSetup setup) => Setup = setup; + public DefaultMockBehavior(IMockSetup setup) => Setup = setup; - public ObservableCollection Behaviors { get; } = new ObservableCollection(); + public ObservableCollection Behaviors { get; } = new ObservableCollection(); public IMockSetup Setup { get; } diff --git a/src/Moq/Moq.Sdk/GlobalSuppressions.cs b/src/Moq/Moq.Sdk/GlobalSuppressions.cs new file mode 100644 index 00000000..5a8ec8b0 --- /dev/null +++ b/src/Moq/Moq.Sdk/GlobalSuppressions.cs @@ -0,0 +1,8 @@ + +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Redundancies in Symbol Declarations", "RECS0154:Parameter is never used", Justification = "False positive")] + diff --git a/src/Moq/Moq.Sdk/IBehavior.cs b/src/Moq/Moq.Sdk/IBehavior.cs new file mode 100644 index 00000000..6e553427 --- /dev/null +++ b/src/Moq/Moq.Sdk/IBehavior.cs @@ -0,0 +1,18 @@ +using Stunts; + +namespace Moq.Sdk +{ + /// + /// Represents a unit of behavior (such as returning a value, + /// invoking a callback or throwing an exception) that applies + /// to a mock when a given setup is matched (such as a particular + /// method being called with specific arguments). + /// + public interface IBehavior + { + /// + /// The delegate that implements the behavior. + /// + InvokeBehavior Invoke { get; } + } +} \ No newline at end of file diff --git a/src/Moq/Moq.Sdk/IMock.cs b/src/Moq/Moq.Sdk/IMock.cs index 0c8b51a7..87df2b51 100644 --- a/src/Moq/Moq.Sdk/IMock.cs +++ b/src/Moq/Moq.Sdk/IMock.cs @@ -11,7 +11,7 @@ public interface IMock : IStunt /// /// Returns a for the given . /// - /// The setup that matches the returned . + /// The setup that equals the returned . IMockBehavior BehaviorFor(IMockSetup setup); /// @@ -19,6 +19,11 @@ public interface IMock : IStunt /// IList Invocations { get; } + /// + /// The mock object this introspection data belongs to. + /// + object Object { get; } + /// /// Arbitrary state associated with a mock instance. /// diff --git a/src/Moq/Moq.Sdk/IMockBehavior.cs b/src/Moq/Moq.Sdk/IMockBehavior.cs index f49c18d1..04238b8d 100644 --- a/src/Moq/Moq.Sdk/IMockBehavior.cs +++ b/src/Moq/Moq.Sdk/IMockBehavior.cs @@ -7,7 +7,7 @@ namespace Moq.Sdk /// An that applies a set of behaviors /// selectively when the current invocation satisfies the /// method for - /// the current . + /// this instance' . /// public interface IMockBehavior : IStuntBehavior { @@ -15,7 +15,7 @@ public interface IMockBehavior : IStuntBehavior /// List of behaviors that should be executed whenever the /// current invocation matches the given setup. /// - ObservableCollection Behaviors { get; } + ObservableCollection Behaviors { get; } /// /// The setup corresponding to this behavior. diff --git a/src/Moq/Moq.Sdk/InvocationBehavior.cs b/src/Moq/Moq.Sdk/InvocationBehavior.cs deleted file mode 100644 index ddf69a03..00000000 --- a/src/Moq/Moq.Sdk/InvocationBehavior.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Stunts; - -namespace Moq.Sdk -{ - public class InvocationBehavior - { - public InvocationBehavior(InvokeBehavior invoke, string name = null, string kind = null) - { - Invoke = invoke; - Name = name; - Kind = kind; - } - - public InvokeBehavior Invoke { get; } - - public string Name { get; } - - public string Kind { get; } - - public override string ToString() => Kind + ":" + (Name ?? ""); - } -} \ No newline at end of file diff --git a/src/Moq/Moq.Sdk/MockExtensions.cs b/src/Moq/Moq.Sdk/MockExtensions.cs index d792982c..31d940b8 100644 --- a/src/Moq/Moq.Sdk/MockExtensions.cs +++ b/src/Moq/Moq.Sdk/MockExtensions.cs @@ -10,61 +10,34 @@ namespace Moq.Sdk public static class MockExtensions { /// - /// Adds a behavior to a mock. + /// Adds a behavior to a mock for the current setup. /// - public static IMocked AddBehavior(this IMocked mock, InvokeBehavior behavior, string name = null) - { - mock.Mock - .BehaviorFor(MockContext.CurrentSetup ?? throw new InvalidOperationException(Strings.NoCurrentSetup)) - .Behaviors - .Add(new InvocationBehavior(behavior, name)); - - return mock; - } - - /// - /// Inserts a behavior into the mock behavior pipeline at the specified - /// index. - /// - public static IMocked InsertBehavior(this IMocked mock, int index, InvokeBehavior behavior, string name = null) - { - mock.Mock - .BehaviorFor(MockContext.CurrentSetup ?? throw new InvalidOperationException(Strings.NoCurrentSetup)) - .Behaviors - .Insert(index, new InvocationBehavior(behavior, name)); - - return mock; - } - - /// - /// Adds a behavior to a mock. - /// - public static IMock AddBehavior(this IMock mock, InvokeBehavior behavior, string name = null) + public static IMock AddBehavior(this IMock mock, InvokeBehavior behavior, Lazy displayName) { mock .BehaviorFor(MockContext.CurrentSetup ?? throw new InvalidOperationException(Strings.NoCurrentSetup)) .Behaviors - .Add(new InvocationBehavior(behavior, name)); - + .Add(new Behavior(behavior, displayName)); + return mock; } /// /// Inserts a behavior into the mock behavior pipeline at the specified - /// index. + /// index for the current setup. /// - public static IMock InsertBehavior(this IMock mock, int index, InvokeBehavior behavior, string name = null) + public static IMock InsertBehavior(this IMock mock, int index, InvokeBehavior behavior, Lazy displayName) { mock .BehaviorFor(MockContext.CurrentSetup ?? throw new InvalidOperationException(Strings.NoCurrentSetup)) .Behaviors - .Insert(index, new InvocationBehavior(behavior, name)); + .Insert(index, new Behavior(behavior, displayName)); return mock; } /// - /// Adds a behavior to a mock. + /// Adds a mock behavior to a mock. /// public static TMock AddBehavior(this TMock mock, IMockBehavior behavior) { @@ -79,7 +52,7 @@ public static TMock AddBehavior(this TMock mock, IMockBehavior behavior) } /// - /// Inserts a behavior into the mock behasvior pipeline at the specified index. + /// Inserts a mock behavior into the mock behavior pipeline at the specified index. /// public static TMock InsertBehavior(this TMock mock, int index, IMockBehavior behavior) { diff --git a/src/Moq/Moq.Sdk/MockFactory.cs b/src/Moq/Moq.Sdk/MockFactory.cs index 191d00ce..b103f6e4 100644 --- a/src/Moq/Moq.Sdk/MockFactory.cs +++ b/src/Moq/Moq.Sdk/MockFactory.cs @@ -1,6 +1,5 @@ using System; using System.Reflection; -using Stunts; namespace Moq.Sdk { @@ -16,7 +15,7 @@ public class MockFactory : IMockFactory /// public static IMockFactory Default { get; set; } = new MockFactory(); - private MockFactory() { } + MockFactory() { } /// /// See diff --git a/src/Moq/Moq.Tests/Mocks/ICalculatorMemoryMock.cs b/src/Moq/Moq.Tests/Mocks/ICalculatorMemoryMock.cs index 542f18e5..068d62e4 100644 --- a/src/Moq/Moq.Tests/Mocks/ICalculatorMemoryMock.cs +++ b/src/Moq/Moq.Tests/Mocks/ICalculatorMemoryMock.cs @@ -44,7 +44,7 @@ public partial class ICalculatorMemoryMock : ICalculatorMemory, IStunt, IMocked IMock mock; [CompilerGenerated] - IMock IMocked.Mock => LazyInitializer.EnsureInitialized(ref mock, () => new MockInfo(this)); + IMock IMocked.Mock => LazyInitializer.EnsureInitialized(ref mock, () => new DefaultMock(this)); #endregion } } \ No newline at end of file diff --git a/src/Moq/Moq.Tests/Mocks/ICalculatorMock.cs b/src/Moq/Moq.Tests/Mocks/ICalculatorMock.cs index 179cf1b0..23cfa795 100644 --- a/src/Moq/Moq.Tests/Mocks/ICalculatorMock.cs +++ b/src/Moq/Moq.Tests/Mocks/ICalculatorMock.cs @@ -75,7 +75,7 @@ public bool TryAdd(ref int x, ref int y, out int z) IMock mock; [CompilerGenerated] - IMock IMocked.Mock => LazyInitializer.EnsureInitialized(ref mock, () => new MockInfo(this)); + IMock IMocked.Mock => LazyInitializer.EnsureInitialized(ref mock, () => new DefaultMock(this)); #endregion } } \ No newline at end of file diff --git a/src/Moq/Moq.Tests/Mocks/INotifyPropertyChangedMock.cs b/src/Moq/Moq.Tests/Mocks/INotifyPropertyChangedMock.cs index e253633c..08149484 100644 --- a/src/Moq/Moq.Tests/Mocks/INotifyPropertyChangedMock.cs +++ b/src/Moq/Moq.Tests/Mocks/INotifyPropertyChangedMock.cs @@ -34,7 +34,7 @@ public partial class INotifyPropertyChangedMock : INotifyPropertyChanged, IStunt #region IMocked IMock mock; - IMock IMocked.Mock => LazyInitializer.EnsureInitialized(ref mock, () => new MockInfo(this)); + IMock IMocked.Mock => LazyInitializer.EnsureInitialized(ref mock, () => new DefaultMock(this)); #endregion } } \ No newline at end of file diff --git a/src/Moq/Moq.Tests/Mocks/IRecursiveBranchMock.cs b/src/Moq/Moq.Tests/Mocks/IRecursiveBranchMock.cs index aed6a22c..5ea0eb48 100644 --- a/src/Moq/Moq.Tests/Mocks/IRecursiveBranchMock.cs +++ b/src/Moq/Moq.Tests/Mocks/IRecursiveBranchMock.cs @@ -39,7 +39,7 @@ public partial class IRecursiveBranchMock : IRecursiveBranch, IStunt, IMocked IMock mock; [CompilerGenerated] - IMock IMocked.Mock => LazyInitializer.EnsureInitialized(ref mock, () => new MockInfo(this)); + IMock IMocked.Mock => LazyInitializer.EnsureInitialized(ref mock, () => new DefaultMock(this)); #endregion } } \ No newline at end of file diff --git a/src/Moq/Moq.Tests/Mocks/IRecursiveLeafMock.cs b/src/Moq/Moq.Tests/Mocks/IRecursiveLeafMock.cs index 1544c20d..89561228 100644 --- a/src/Moq/Moq.Tests/Mocks/IRecursiveLeafMock.cs +++ b/src/Moq/Moq.Tests/Mocks/IRecursiveLeafMock.cs @@ -39,7 +39,7 @@ public partial class IRecursiveLeafMock : IRecursiveLeaf, IStunt, IMocked IMock mock; [CompilerGenerated] - IMock IMocked.Mock => LazyInitializer.EnsureInitialized(ref mock, () => new MockInfo(this)); + IMock IMocked.Mock => LazyInitializer.EnsureInitialized(ref mock, () => new DefaultMock(this)); #endregion } } \ No newline at end of file diff --git a/src/Moq/Moq.Tests/Mocks/IRecursiveRootMock.cs b/src/Moq/Moq.Tests/Mocks/IRecursiveRootMock.cs index 2ee2440e..1837bfd9 100644 --- a/src/Moq/Moq.Tests/Mocks/IRecursiveRootMock.cs +++ b/src/Moq/Moq.Tests/Mocks/IRecursiveRootMock.cs @@ -39,7 +39,7 @@ public partial class IRecursiveRootMock : IRecursiveRoot, IStunt, IMocked IMock mock; [CompilerGenerated] - IMock IMocked.Mock => LazyInitializer.EnsureInitialized(ref mock, () => new MockInfo(this)); + IMock IMocked.Mock => LazyInitializer.EnsureInitialized(ref mock, () => new DefaultMock(this)); #endregion } } \ No newline at end of file diff --git a/src/Moq/Moq/CallbackExtension.cs b/src/Moq/Moq/CallbackExtension.cs index 1e221031..955d92be 100644 --- a/src/Moq/Moq/CallbackExtension.cs +++ b/src/Moq/Moq/CallbackExtension.cs @@ -27,7 +27,7 @@ static TResult Callback(this TResult target, Action 0) { var wrapped = behavior.Behaviors.Pop(); - behavior.Behaviors.Add(new InvocationBehavior( + behavior.Behaviors.Add(new Behavior( (mi, next) => { // If the wrapped target does not invoke the next @@ -50,20 +50,17 @@ static TResult Callback(this TResult target, Action { callback(mi.Arguments); return next()(mi, next); - }, - "Callback", "Callback") + }, "Callback") ); } } diff --git a/src/Moq/Moq/Extensions.cs b/src/Moq/Moq/Extensions.cs index c7325497..c19fcc23 100644 --- a/src/Moq/Moq/Extensions.cs +++ b/src/Moq/Moq/Extensions.cs @@ -1,9 +1,6 @@ using Stunts; -using Moq.Sdk; using System.Reflection; -using System.ComponentModel; using System; -using System.Linq; namespace Moq { diff --git a/src/Moq/Moq/MockedExtensions.cs b/src/Moq/Moq/MockedExtensions.cs new file mode 100644 index 00000000..1b4620f5 --- /dev/null +++ b/src/Moq/Moq/MockedExtensions.cs @@ -0,0 +1,35 @@ +using System.ComponentModel; +using Moq.Sdk; +using Stunts; + +namespace Moq +{ + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static class MockedExtensions + { + /// + /// Clears any existing behavior (including any setups) and + /// adds the necessary behaviors to the so + /// that it behaves as specified by the + /// enumeration. + /// + /// + /// This method can be used by custom mocks to ensure they have the + /// same default behaviors as a mock created using Mock.Of{T}. + /// + public static void SetBehavior(this IMocked mock, MockBehavior behavior) + { + mock.Mock.Behaviors.Clear(); + + mock.Mock.Behaviors.Add(new MockTrackingBehavior()); + mock.Mock.Behaviors.Add(new EventBehavior()); + mock.Mock.Behaviors.Add(new PropertyBehavior { SetterRequiresSetup = behavior == MockBehavior.Strict }); + mock.Mock.Behaviors.Add(new DefaultEqualityBehavior()); + + if (behavior == MockBehavior.Strict) + mock.Mock.Behaviors.Add(new StrictMockBehavior()); + else + mock.Mock.Behaviors.Add(new DefaultValueBehavior()); + } + } +} diff --git a/src/Moq/Moq/ReturnsBehavior.cs b/src/Moq/Moq/ReturnsBehavior.cs new file mode 100644 index 00000000..500781a8 --- /dev/null +++ b/src/Moq/Moq/ReturnsBehavior.cs @@ -0,0 +1,24 @@ +using System; +using Stunts; +using Moq.Sdk; +using System.Diagnostics; + +namespace Moq +{ + /// + /// A custom invocation info for returning values, so that + /// the actual value to return can be replaced on succesive + /// method calls. + /// + [DebuggerDisplay("Returns {ReturnValue}")] + class ReturnsBehavior : IBehavior + { + public ReturnsBehavior(Func valueGetter) + => ValueGetter = valueGetter; + + public Func ValueGetter { get; set; } + + public InvokeBehavior Invoke => (IMethodInvocation invocation, GetNextBehavior getNext) + => invocation.CreateValueReturn(ValueGetter()); + } +} diff --git a/src/Moq/Moq/ReturnsExtension.Overloads.cs b/src/Moq/Moq/ReturnsExtension.Overloads.cs index 366e4cc3..8ca1b586 100644 --- a/src/Moq/Moq/ReturnsExtension.Overloads.cs +++ b/src/Moq/Moq/ReturnsExtension.Overloads.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; namespace Moq { diff --git a/src/Moq/Moq/ReturnsExtension.cs b/src/Moq/Moq/ReturnsExtension.cs index 7c80d66c..a2ce3c2d 100644 --- a/src/Moq/Moq/ReturnsExtension.cs +++ b/src/Moq/Moq/ReturnsExtension.cs @@ -23,18 +23,14 @@ public static TResult Returns(this TResult target, TResult value) var mock = ((IMocked)setup.Invocation.Target).Mock; mock.Invocations.Remove(setup.Invocation); var behavior = mock.BehaviorFor(setup); - var returnBehavior = behavior.Behaviors.OfType().FirstOrDefault(); + var returnBehavior = behavior.Behaviors.OfType().FirstOrDefault(); if (returnBehavior != null) { - returnBehavior.ReturnValue.ValueGetter = () => value; + returnBehavior.ValueGetter = () => value; } else { - var returnValue = new ReturnValue(() => value); - behavior.Behaviors.Add(new ReturnsInvocationBehavior( - (mi, next) => mi.CreateValueReturn(returnValue.ValueGetter(), mi.Arguments), - returnValue) - ); + behavior.Behaviors.Add(new ReturnsBehavior(() => value)); } } @@ -54,19 +50,14 @@ public static TResult Returns(this TResult target, Func value) var mock = ((IMocked)setup.Invocation.Target).Mock; mock.Invocations.Remove(setup.Invocation); var behavior = mock.BehaviorFor(setup); - var returnBehavior = behavior.Behaviors.OfType().FirstOrDefault(); + var returnBehavior = behavior.Behaviors.OfType().FirstOrDefault(); if (returnBehavior != null) { - returnBehavior.ReturnValue.ValueGetter = () => value(); + returnBehavior.ValueGetter = () => value(); } else { - var returnValue = new ReturnValue(() => value()); - behavior.Behaviors.Add(new ReturnsInvocationBehavior( - (mi, next) => mi.CreateValueReturn(returnValue.ValueGetter(), mi.Arguments), - returnValue, - "Returns") - ); + behavior.Behaviors.Add(new ReturnsBehavior(() => value())); } } @@ -79,13 +70,16 @@ static TResult Returns(Delegate value, InvokeBehavior behavior) var setup = MockContext.CurrentSetup; if (setup != null) { + // TODO: Is this even necessary given that intellisense gives us + // the rigth compiler safety already? setup.Invocation.EnsureCompatible(value); + var mock = ((IMocked)setup.Invocation.Target).Mock; mock.Invocations.Remove(setup.Invocation); var mockBehavior = mock.BehaviorFor(setup); - mockBehavior.Behaviors.Add(new InvocationBehavior(behavior, "Returns", "Returns")); + mockBehavior.Behaviors.Add(new Behavior(behavior, "Returns(() => ...)")); } return default(TResult); diff --git a/src/Moq/Moq/ReturnsInvocationBehavior.cs b/src/Moq/Moq/ReturnsInvocationBehavior.cs deleted file mode 100644 index cabe6f97..00000000 --- a/src/Moq/Moq/ReturnsInvocationBehavior.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using Stunts; -using Moq.Sdk; - -namespace Moq -{ - internal class ReturnsInvocationBehavior : InvocationBehavior - { - public ReturnsInvocationBehavior(InvokeBehavior invoke, ReturnValue value, string name = null) : base(invoke, name, "Returns") - => ReturnValue = value; - - public ReturnValue ReturnValue { get; set; } - } - - internal class ReturnValue - { - public ReturnValue(Func valueGetter) => ValueGetter = valueGetter; - - public Func ValueGetter { get; set; } - } -} diff --git a/src/Moq/Moq/Setup.cs b/src/Moq/Moq/Setup.cs index 80898293..a9d2dc32 100644 --- a/src/Moq/Moq/Setup.cs +++ b/src/Moq/Moq/Setup.cs @@ -1,6 +1,5 @@ using System; using System.ComponentModel; -using System.Linq; using Moq.Properties; using Moq.Sdk; using Stunts; diff --git a/src/Moq/Moq/ThrowsExtension.cs b/src/Moq/Moq/ThrowsExtension.cs index 6f1c10fb..9d819246 100644 --- a/src/Moq/Moq/ThrowsExtension.cs +++ b/src/Moq/Moq/ThrowsExtension.cs @@ -23,10 +23,10 @@ public static void Throws(this object target, Exception exception) mock.Invocations.Remove(setup.Invocation); var behavior = mock.BehaviorFor(setup); - behavior.Behaviors.Add(new InvocationBehavior( + behavior.Behaviors.Add(new Behavior( (mi, next) => mi.CreateExceptionReturn(exception), - "Exception", "Exception") - ); + new Lazy(() => $"Throws<{exception.GetType().Name}>(\"{exception.Message}\")") + )); } } @@ -44,10 +44,10 @@ public static void Throws(this object target) mock.Invocations.Remove(setup.Invocation); var behavior = mock.BehaviorFor(setup); - behavior.Behaviors.Add(new InvocationBehavior( + behavior.Behaviors.Add(new Behavior( (mi, next) => mi.CreateExceptionReturn(new TException()), - "Exception", "Exception") - ); + new Lazy(() => $"Throws<{typeof(TException).Name}>()") + )); } } } diff --git a/src/Moq/Moq/contentFiles/cs/netstandard1.3/Mock.Overloads.cs b/src/Moq/Moq/contentFiles/cs/netstandard1.3/Mock.Overloads.cs index 4d71cb19..4564f79f 100644 --- a/src/Moq/Moq/contentFiles/cs/netstandard1.3/Mock.Overloads.cs +++ b/src/Moq/Moq/contentFiles/cs/netstandard1.3/Mock.Overloads.cs @@ -1,14 +1,9 @@ -using System; -using System.Reflection; -using Moq.Sdk; -using Stunts; - namespace Moq { /// /// Instantiates stunts for the specified types. /// - internal partial class Mock + partial class Mock { /// /// Creates a mock that inherits or implements the type . diff --git a/src/Moq/Moq/contentFiles/cs/netstandard1.3/Mock.cs b/src/Moq/Moq/contentFiles/cs/netstandard1.3/Mock.cs index 4f014efb..b9d1860d 100644 --- a/src/Moq/Moq/contentFiles/cs/netstandard1.3/Mock.cs +++ b/src/Moq/Moq/contentFiles/cs/netstandard1.3/Mock.cs @@ -1,33 +1,23 @@ using System; using System.Reflection; using Moq.Sdk; -using Stunts; namespace Moq { /// - /// Instantiates stunts for the specified types. + /// Instantiates mocks for the specified types. /// - internal partial class Mock + partial class Mock { + /// + /// Creates the mock instance by using the specified types to + /// lookup the mock type in the assembly defining this class. + /// static T Create(MockBehavior behavior, object[] constructorArgs, params Type[] interfaces) { var mocked = (IMocked)MockFactory.Default.CreateMock(typeof(Mock).GetTypeInfo().Assembly, typeof(T), interfaces, constructorArgs); - mocked.Mock.Behaviors.Add(new MockTrackingBehavior()); - - if (behavior == MockBehavior.Strict) - { - mocked.Mock.Behaviors.Add(new PropertyBehavior { SetterRequiresSetup = true }); - mocked.Mock.Behaviors.Add(new StrictMockBehavior()); - } - else - { - mocked.Mock.Behaviors.Add(new PropertyBehavior()); - mocked.Mock.Behaviors.Add(new DefaultValueBehavior()); - } - - mocked.Mock.Behaviors.Add(new DefaultEqualityBehavior()); + mocked.SetBehavior(behavior); return (T)mocked; } diff --git a/src/Moq/Moq/contentFiles/vb/netstandard1.3/Mock.vb b/src/Moq/Moq/contentFiles/vb/netstandard1.3/Mock.vb index dab95404..6018fd02 100644 --- a/src/Moq/Moq/contentFiles/vb/netstandard1.3/Mock.vb +++ b/src/Moq/Moq/contentFiles/vb/netstandard1.3/Mock.vb @@ -1,6 +1,5 @@ Imports System.Reflection Imports Moq.Sdk -Imports Stunts Namespace Global.Moq @@ -9,17 +8,7 @@ Namespace Global.Moq Private Shared Function Create(Of T)(ByVal behavior As MockBehavior, ByVal constructorArgs As Object(), ParamArray interfaces As Type()) As T Dim mocked = DirectCast(MockFactory.[Default].CreateMock(GetType(Mock).GetTypeInfo().Assembly, GetType(T), interfaces, constructorArgs), IMocked) - mocked.Mock.Behaviors.Add(New MockTrackingBehavior()) - - If behavior = MockBehavior.Strict Then - mocked.Mock.Behaviors.Add(New PropertyBehavior() With {.SetterRequiresSetup = True}) - mocked.Mock.Behaviors.Add(New StrictMockBehavior()) - Else - mocked.Mock.Behaviors.Add(New PropertyBehavior()) - mocked.Mock.Behaviors.Add(New DefaultValueBehavior()) - End If - - mocked.Mock.Behaviors.Add(New DefaultEqualityBehavior()) + mocked.SetBehavior(behavior) Return DirectCast(mocked, T) End Function diff --git a/src/Samples/Moq.CSharp/Class1.cs b/src/Samples/Moq.CSharp/Class1.cs index 2b11df9b..77eb261e 100644 --- a/src/Samples/Moq.CSharp/Class1.cs +++ b/src/Samples/Moq.CSharp/Class1.cs @@ -1,10 +1,6 @@ using System; -using System.ComponentModel; -using Mocks; using Moq; -using static Moq.Syntax; using Moq.Sdk; -using Stunts; namespace Sample.CSharp { @@ -16,12 +12,10 @@ public void Test() fake.Memory.Recall().Returns(5); - //var m = new FooMock(); - //m.AddBehavior(new MockTrackingBehavior()); - //m.AddBehavior(new EventBehavior()); - //m.AddBehavior(new PropertyBehavior()); - //m.AddBehavior(new DefaultValueBehavior()); - //m.AddBehavior(new DefaultEqualityBehavior()); + var m = new FooMock(); + m.SetBehavior(MockBehavior.Loose); + + //((IMocked)m). //var name = ""; diff --git a/src/Samples/Moq.CSharp/FooMock.cs b/src/Samples/Moq.CSharp/FooMock.cs index 15a125cf..8e228efe 100644 --- a/src/Samples/Moq.CSharp/FooMock.cs +++ b/src/Samples/Moq.CSharp/FooMock.cs @@ -25,7 +25,7 @@ public class FooMock : IStunt, IMocked, IDisposable, IFormatProvider, INotifyPro ObservableCollection IStunt.Behaviors => pipeline.Behaviors; [CompilerGenerated] - IMock IMocked.Mock => LazyInitializer.EnsureInitialized(ref mock, () => new MockInfo(this)); + IMock IMocked.Mock => LazyInitializer.EnsureInitialized(ref mock, () => new DefaultMock(this)); [CompilerGenerated] public void Dispose() => pipeline.Execute(new MethodInvocation(this, MethodBase.GetCurrentMethod())); diff --git a/src/Samples/Moq.CSharp/Mocks/CalculatorBaseICustomFormatterIDisposableMock.cs b/src/Samples/Moq.CSharp/Mocks/CalculatorBaseICustomFormatterIDisposableMock.cs index 01c3ccfc..82a7c1e4 100644 --- a/src/Samples/Moq.CSharp/Mocks/CalculatorBaseICustomFormatterIDisposableMock.cs +++ b/src/Samples/Moq.CSharp/Mocks/CalculatorBaseICustomFormatterIDisposableMock.cs @@ -26,7 +26,7 @@ public partial class CalculatorBaseICustomFormatterIDisposableMock : CalculatorB [CompilerGenerated] ObservableCollection IStunt.Behaviors => pipeline.Behaviors; - IMock IMocked.Mock => LazyInitializer.EnsureInitialized(ref mock, () => new MockInfo(this)); + IMock IMocked.Mock => LazyInitializer.EnsureInitialized(ref mock, () => new DefaultMock(this)); [CompilerGenerated] public override int? this[string name] { get => pipeline.Execute(new MethodInvocation(this, MethodBase.GetCurrentMethod(), name)); set => pipeline.Execute(new MethodInvocation(this, MethodBase.GetCurrentMethod(), name, value)); } diff --git a/src/Samples/Moq.CSharp/Mocks/IBarMock.cs b/src/Samples/Moq.CSharp/Mocks/IBarMock.cs index 785668ec..f4e7f4d8 100644 --- a/src/Samples/Moq.CSharp/Mocks/IBarMock.cs +++ b/src/Samples/Moq.CSharp/Mocks/IBarMock.cs @@ -25,7 +25,7 @@ public partial class IBarMock : IBar, IStunt, IMocked [CompilerGenerated] ObservableCollection IStunt.Behaviors => pipeline.Behaviors; - IMock IMocked.Mock => LazyInitializer.EnsureInitialized(ref mock, () => new MockInfo(this)); + IMock IMocked.Mock => LazyInitializer.EnsureInitialized(ref mock, () => new DefaultMock(this)); [CompilerGenerated] public void DoBar() => pipeline.Execute(new MethodInvocation(this, MethodBase.GetCurrentMethod())); diff --git a/src/Samples/Moq.CSharp/Mocks/ICalculatorMemoryMock.cs b/src/Samples/Moq.CSharp/Mocks/ICalculatorMemoryMock.cs index 2782b08a..b22c0cf2 100644 --- a/src/Samples/Moq.CSharp/Mocks/ICalculatorMemoryMock.cs +++ b/src/Samples/Moq.CSharp/Mocks/ICalculatorMemoryMock.cs @@ -26,7 +26,7 @@ public partial class ICalculatorMemoryMock : ICalculatorMemory, IStunt, IMocked [CompilerGenerated] ObservableCollection IStunt.Behaviors => pipeline.Behaviors; - IMock IMocked.Mock => LazyInitializer.EnsureInitialized(ref mock, () => new MockInfo(this)); + IMock IMocked.Mock => LazyInitializer.EnsureInitialized(ref mock, () => new DefaultMock(this)); [CompilerGenerated] public void Add(int value) => pipeline.Execute(new MethodInvocation(this, MethodBase.GetCurrentMethod(), value)); diff --git a/src/Samples/Moq.CSharp/Mocks/ICalculatorMock.cs b/src/Samples/Moq.CSharp/Mocks/ICalculatorMock.cs index a1f98e3e..29b4f7db 100644 --- a/src/Samples/Moq.CSharp/Mocks/ICalculatorMock.cs +++ b/src/Samples/Moq.CSharp/Mocks/ICalculatorMock.cs @@ -26,7 +26,7 @@ public partial class ICalculatorMock : ICalculator, IStunt, IMocked [CompilerGenerated] ObservableCollection IStunt.Behaviors => pipeline.Behaviors; - IMock IMocked.Mock => LazyInitializer.EnsureInitialized(ref mock, () => new MockInfo(this)); + IMock IMocked.Mock => LazyInitializer.EnsureInitialized(ref mock, () => new DefaultMock(this)); [CompilerGenerated] public int? this[string name] { get => pipeline.Execute(new MethodInvocation(this, MethodBase.GetCurrentMethod(), name)); set => pipeline.Execute(new MethodInvocation(this, MethodBase.GetCurrentMethod(), name, value)); } diff --git a/src/Samples/Moq.CSharp/Mocks/ICustomFormatterIDisposableMock.cs b/src/Samples/Moq.CSharp/Mocks/ICustomFormatterIDisposableMock.cs index 2b4e4266..f54fdcd0 100644 --- a/src/Samples/Moq.CSharp/Mocks/ICustomFormatterIDisposableMock.cs +++ b/src/Samples/Moq.CSharp/Mocks/ICustomFormatterIDisposableMock.cs @@ -25,7 +25,7 @@ public partial class ICustomFormatterIDisposableMock : ICustomFormatter, IDispos [CompilerGenerated] ObservableCollection IStunt.Behaviors => pipeline.Behaviors; - IMock IMocked.Mock => LazyInitializer.EnsureInitialized(ref mock, () => new MockInfo(this)); + IMock IMocked.Mock => LazyInitializer.EnsureInitialized(ref mock, () => new DefaultMock(this)); [CompilerGenerated] public void Dispose() => pipeline.Execute(new MethodInvocation(this, MethodBase.GetCurrentMethod())); diff --git a/src/Samples/Moq.CSharp/Mocks/IFooMock.cs b/src/Samples/Moq.CSharp/Mocks/IFooMock.cs index 38d982a7..a9f86a9c 100644 --- a/src/Samples/Moq.CSharp/Mocks/IFooMock.cs +++ b/src/Samples/Moq.CSharp/Mocks/IFooMock.cs @@ -25,7 +25,7 @@ public partial class IFooMock : IFoo, IStunt, IMocked [CompilerGenerated] ObservableCollection IStunt.Behaviors => pipeline.Behaviors; - IMock IMocked.Mock => LazyInitializer.EnsureInitialized(ref mock, () => new MockInfo(this)); + IMock IMocked.Mock => LazyInitializer.EnsureInitialized(ref mock, () => new DefaultMock(this)); [CompilerGenerated] public string Id => pipeline.Execute(new MethodInvocation(this, MethodBase.GetCurrentMethod())); diff --git a/src/Samples/Moq.VisualBasic/FooMock.vb b/src/Samples/Moq.VisualBasic/FooMock.vb index b5f2844b..d19ce1b2 100644 --- a/src/Samples/Moq.VisualBasic/FooMock.vb +++ b/src/Samples/Moq.VisualBasic/FooMock.vb @@ -33,7 +33,7 @@ Public Class FooMock ReadOnly Property Mock As IMock Implements IMocked.Mock Get - Return LazyInitializer.EnsureInitialized(_mock, (Function() New MockInfo(Me))) + Return LazyInitializer.EnsureInitialized(_mock, (Function() New DefaultMock(Me))) End Get End Property diff --git a/src/Samples/Moq.VisualBasic/Mocks/IBarMock.vb b/src/Samples/Moq.VisualBasic/Mocks/IBarMock.vb index 620963b6..fed8c2c8 100644 --- a/src/Samples/Moq.VisualBasic/Mocks/IBarMock.vb +++ b/src/Samples/Moq.VisualBasic/Mocks/IBarMock.vb @@ -58,7 +58,7 @@ Namespace Global.Mocks ReadOnly Property Mock As IMock Implements IMocked.Mock Get - Return LazyInitializer.EnsureInitialized(_mock, (Function() New MockInfo(Me))) + Return LazyInitializer.EnsureInitialized(_mock, (Function() New DefaultMock(Me))) End Get End Property End Class diff --git a/src/Samples/Moq.VisualBasic/Mocks/ICustomFormatterIDisposableMock.vb b/src/Samples/Moq.VisualBasic/Mocks/ICustomFormatterIDisposableMock.vb index d138f0c3..95a92655 100644 --- a/src/Samples/Moq.VisualBasic/Mocks/ICustomFormatterIDisposableMock.vb +++ b/src/Samples/Moq.VisualBasic/Mocks/ICustomFormatterIDisposableMock.vb @@ -62,7 +62,7 @@ Namespace Global.Mocks ReadOnly Property Mock As IMock Implements IMocked.Mock Get - Return LazyInitializer.EnsureInitialized(_mock, (Function() New MockInfo(Me))) + Return LazyInitializer.EnsureInitialized(_mock, (Function() New DefaultMock(Me))) End Get End Property End Class diff --git a/src/Samples/Moq.VisualBasic/Mocks/IFooMock.vb b/src/Samples/Moq.VisualBasic/Mocks/IFooMock.vb index 34b4ccfe..6090b2f8 100644 --- a/src/Samples/Moq.VisualBasic/Mocks/IFooMock.vb +++ b/src/Samples/Moq.VisualBasic/Mocks/IFooMock.vb @@ -33,7 +33,7 @@ Namespace Global.Mocks ReadOnly Property Mock As IMock Implements IMocked.Mock Get - Return LazyInitializer.EnsureInitialized(_mock, (Function() New MockInfo(Me))) + Return LazyInitializer.EnsureInitialized(_mock, (Function() New DefaultMock(Me))) End Get End Property