Skip to content

Commit

Permalink
Move state (fields) from Mock into Mock<T> (#540)
Browse files Browse the repository at this point in the history
  • Loading branch information
stakx authored Dec 3, 2017
1 parent b4fdfc4 commit 4f5ffb8
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 53 deletions.
12 changes: 7 additions & 5 deletions Source/AsInterface.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,16 +65,14 @@ internal override ConcurrentDictionary<MethodInfo, MockWithWrappedMockObject> In

internal override InvocationCollection Invocations => this.owner.Invocations;

internal override bool IsObjectInitialized => this.owner.IsObjectInitialized;

internal override Type MockedType
{
get { return typeof(TInterface); }
}

public override MockBehavior Behavior
{
get { return this.owner.Behavior; }
internal set { this.owner.Behavior = value; }
}
public override MockBehavior Behavior => this.owner.Behavior;

public override bool CallBase
{
Expand All @@ -90,6 +88,10 @@ public override DefaultValueProvider DefaultValueProvider

internal override EventHandlerCollection EventHandlers => this.owner.EventHandlers;

internal override List<Type> ImplementedInterfaces => this.owner.ImplementedInterfaces;

internal override int InternallyImplementedInterfaceCount => this.owner.InternallyImplementedInterfaceCount;

public override TInterface Object
{
get { return this.owner.Object as TInterface; }
Expand Down
74 changes: 65 additions & 9 deletions Source/Mock.Generic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
// http://www.opensource.org/licenses/bsd-license.php]

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
Expand All @@ -61,10 +62,22 @@ namespace Moq
public partial class Mock<T> : Mock, IMock<T> where T : class
{
private static int serialNumberCounter = 0;

private T instance;
private Dictionary<Type, object> configuredDefaultValues;
private object[] constructorArguments;
private DefaultValueProvider defaultValueProvider;
private EventHandlerCollection eventHandlers;
private List<Type> implementedInterfaces;
private int internallyImplementedInterfaceCount;
private ConcurrentDictionary<MethodInfo, MockWithWrappedMockObject> innerMocks;
private InvocationCollection invocations;
private string name;
private SetupCollection setups;

private MockBehavior behavior;
private bool callBase;
private Switches switches;

#region Ctors

Expand Down Expand Up @@ -114,24 +127,29 @@ public Mock(MockBehavior behavior, params object[] args)
args = new object[] { null };
}

this.Name = GenerateMockName();

this.Behavior = behavior;
this.behavior = behavior;
this.configuredDefaultValues = new Dictionary<Type, object>();
this.constructorArguments = args;
this.defaultValueProvider = DefaultValueProvider.Empty;
this.ImplementedInterfaces.AddRange(typeof(T).GetInterfaces().Where(i => (i.GetTypeInfo().IsPublic || i.GetTypeInfo().IsNestedPublic) && !i.GetTypeInfo().IsImport));
this.ImplementedInterfaces.Add(typeof(IMocked<T>));
this.InternallyImplementedInterfaceCount = this.ImplementedInterfaces.Count;
this.eventHandlers = new EventHandlerCollection();
this.implementedInterfaces = new List<Type>();
this.implementedInterfaces.AddRange(typeof(T).GetInterfaces().Where(i => (i.GetTypeInfo().IsPublic || i.GetTypeInfo().IsNestedPublic) && !i.GetTypeInfo().IsImport));
this.implementedInterfaces.Add(typeof(IMocked<T>));
this.internallyImplementedInterfaceCount = this.ImplementedInterfaces.Count;
this.innerMocks = new ConcurrentDictionary<MethodInfo, MockWithWrappedMockObject>();
this.invocations = new InvocationCollection();
this.name = CreateUniqueDefaultMockName();
this.setups = new SetupCollection();
this.switches = Switches.Default;

this.CheckParameters();
}

private string GenerateMockName()
private static string CreateUniqueDefaultMockName()
{
var serialNumber = Interlocked.Increment(ref serialNumberCounter).ToString("x8");

var typeName = typeof (T).FullName;
string typeName = typeof (T).FullName;

#if FEATURE_CODEDOM
if (typeof (T).IsGenericType)
Expand Down Expand Up @@ -168,6 +186,16 @@ private void CheckParameters()

#region Properties

/// <include file='Mock.xdoc' path='docs/doc[@for="Mock.Behavior"]/*'/>
public override MockBehavior Behavior => this.behavior;

/// <include file='Mock.xdoc' path='docs/doc[@for="Mock.CallBase"]/*'/>
public override bool CallBase
{
get => this.callBase;
set => this.callBase = value;
}

internal override Dictionary<Type, object> ConfiguredDefaultValues => this.configuredDefaultValues;

/// <summary>
Expand All @@ -180,6 +208,18 @@ public override DefaultValueProvider DefaultValueProvider
set => this.defaultValueProvider = value ?? throw new ArgumentNullException(nameof(value));
}

internal override EventHandlerCollection EventHandlers => this.eventHandlers;

internal override ConcurrentDictionary<MethodInfo, MockWithWrappedMockObject> InnerMocks => this.innerMocks;

internal override List<Type> ImplementedInterfaces => this.implementedInterfaces;

internal override int InternallyImplementedInterfaceCount => this.internallyImplementedInterfaceCount;

internal override InvocationCollection Invocations => this.invocations;

internal override bool IsObjectInitialized => this.instance != null;

/// <include file='Mock.Generic.xdoc' path='docs/doc[@for="Mock{T}.Object"]/*'/>
[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.")]
Expand All @@ -189,7 +229,11 @@ public override DefaultValueProvider DefaultValueProvider
}

/// <include file='Mock.Generic.xdoc' path='docs/doc[@for="Mock{T}.Name"]/*'/>
public string Name { get; set; }
public string Name
{
get => this.name;
set => this.name = value;
}

/// <include file='Mock.Generic.xdoc' path='docs/doc[@for="Mock{T}.ToString"]/*'/>
public override string ToString()
Expand Down Expand Up @@ -272,8 +316,20 @@ internal override Type MockedType
get { return typeof(T); }
}

internal override SetupCollection Setups => this.setups;

internal override Type TargetType => typeof(T);

/// <summary>
/// A set of switches that influence how this mock will operate.
/// You can opt in or out of certain features via this property.
/// </summary>
public override Switches Switches
{
get => this.switches;
set => this.switches = value;
}

#endregion

#region Setup
Expand Down
54 changes: 15 additions & 39 deletions Source/Mock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,21 +59,9 @@ namespace Moq
/// <include file='Mock.xdoc' path='docs/doc[@for="Mock"]/*'/>
public abstract partial class Mock : IFluentInterface
{
private bool isInitialized;
private EventHandlerCollection eventHandlers;
private InvocationCollection invocations;
private SetupCollection setups;
private Switches switches;

/// <include file='Mock.xdoc' path='docs/doc[@for="Mock.ctor"]/*'/>
protected Mock()
{
this.eventHandlers = new EventHandlerCollection();
this.ImplementedInterfaces = new List<Type>();
this.InnerMocks = new ConcurrentDictionary<MethodInfo, MockWithWrappedMockObject>();
this.invocations = new InvocationCollection();
this.setups = new SetupCollection();
this.switches = Switches.Default;
}

/// <include file='Mock.xdoc' path='docs/doc[@for="Mock.Get"]/*'/>
Expand Down Expand Up @@ -131,7 +119,7 @@ public static Mock<T> Get<T>(T mocked) where T : class

throw new ArgumentException(Resources.ObjectInstanceNotMock, "mocked");
}

/// <include file='Mock.xdoc' path='docs/doc[@for="Mock.Verify"]/*'/>
public static void Verify(params Mock[] mocks)
{
Expand All @@ -140,7 +128,7 @@ public static void Verify(params Mock[] mocks)
mock.Verify();
}
}

/// <include file='Mock.xdoc' path='docs/doc[@for="Mock.VerifyAll"]/*'/>
public static void VerifyAll(params Mock[] mocks)
{
Expand All @@ -151,10 +139,10 @@ public static void VerifyAll(params Mock[] mocks)
}

/// <include file='Mock.xdoc' path='docs/doc[@for="Mock.Behavior"]/*'/>
public virtual MockBehavior Behavior { get; internal set; }
public abstract MockBehavior Behavior { get; }

/// <include file='Mock.xdoc' path='docs/doc[@for="Mock.CallBase"]/*'/>
public virtual bool CallBase { get; set; }
public abstract bool CallBase { get; set; }

/// <include file='Mock.xdoc' path='docs/doc[@for="Mock.DefaultValue"]/*'/>
public DefaultValue DefaultValue
Expand All @@ -181,26 +169,18 @@ public DefaultValue DefaultValue
}
}

internal virtual EventHandlerCollection EventHandlers => this.eventHandlers;
internal abstract EventHandlerCollection EventHandlers { get; }

/// <include file='Mock.xdoc' path='docs/doc[@for="Mock.Object"]/*'/>
[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(); }
}
public object Object => this.OnGetObject();

private object GetObject()
{
var value = this.OnGetObject();
this.isInitialized = true;
return value;
}
internal abstract ConcurrentDictionary<MethodInfo, MockWithWrappedMockObject> InnerMocks { get; }

internal virtual ConcurrentDictionary<MethodInfo, MockWithWrappedMockObject> InnerMocks { get; private set; }
internal abstract bool IsObjectInitialized { get; }

internal virtual InvocationCollection Invocations => this.invocations;
internal abstract InvocationCollection Invocations { get; }

/// <include file='Mock.xdoc' path='docs/doc[@for="Mock.OnGetObject"]/*'/>
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This is actually the protected virtual implementation of the property Object.")]
Expand Down Expand Up @@ -235,25 +215,21 @@ private object GetObject()
/// <summary>
/// Exposes the list of extra interfaces implemented by the mock.
/// </summary>
internal List<Type> ImplementedInterfaces { get; private set; }
internal abstract List<Type> ImplementedInterfaces { get; }

/// <summary>
/// Indicates the number of interfaces in <see cref="ImplementedInterfaces"/> that were
/// defined internally, rather than through calls to <see cref="As{TInterface}"/>.
/// </summary>
internal protected int InternallyImplementedInterfaceCount { get; protected set; }
internal abstract int InternallyImplementedInterfaceCount { get; }

internal virtual SetupCollection Setups => this.setups;
internal abstract SetupCollection Setups { get; }

/// <summary>
/// A set of switches that influence how this mock will operate.
/// You can opt in or out of certain features via this property.
/// </summary>
public virtual Switches Switches
{
get => this.switches;
set => this.switches = value;
}
public abstract Switches Switches { get; set; }

internal abstract Type TargetType { get; }

Expand Down Expand Up @@ -416,7 +392,7 @@ private static bool AreSameMethod(Expression left, Expression right)

internal static void VerifyNoOtherCalls(Mock mock)
{
var unverifiedInvocations = mock.invocations.ToArray(invocation => !invocation.Verified);
var unverifiedInvocations = mock.Invocations.ToArray(invocation => !invocation.Verified);
if (unverifiedInvocations.Any())
{
throw new MockException(
Expand Down Expand Up @@ -1158,7 +1134,7 @@ public virtual Mock<TInterface> As<TInterface>()
var index = this.ImplementedInterfaces.LastIndexOf(typeof(TInterface));

var isImplemented = index >= 0;
if (this.isInitialized && !isImplemented)
if (this.IsObjectInitialized && !isImplemented)
{
throw new InvalidOperationException(Resources.AlreadyInitialized);
}
Expand Down

0 comments on commit 4f5ffb8

Please sign in to comment.