-
-
Notifications
You must be signed in to change notification settings - Fork 802
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Give mocks names #76
Give mocks names #76
Changes from 4 commits
da5bd5f
d8b30e0
c0f5262
89f063d
f6748bc
9e4e589
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -39,8 +39,10 @@ | |
// http://www.opensource.org/licenses/bsd-license.php] | ||
|
||
using System; | ||
using System.CodeDom; | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Linq.Expressions; | ||
using Microsoft.CSharp; | ||
using Moq.Language.Flow; | ||
using Moq.Proxy; | ||
using Moq.Language; | ||
|
@@ -102,6 +104,8 @@ public Mock(MockBehavior behavior, params object[] args) | |
args = new object[] { null }; | ||
} | ||
|
||
this.Name = GenerateMockName(); | ||
|
||
this.Behavior = behavior; | ||
this.Interceptor = new Interceptor(behavior, typeof(T), this); | ||
this.constructorArguments = args; | ||
|
@@ -110,6 +114,24 @@ public Mock(MockBehavior behavior, params object[] args) | |
this.CheckParameters(); | ||
} | ||
|
||
private string GenerateMockName() | ||
{ | ||
var randomId = Guid.NewGuid().ToString("N").Substring(0, 4); | ||
|
||
var typeName = typeof (T).FullName; | ||
|
||
if (typeof (T).IsGenericType) | ||
{ | ||
using (var provider = new CSharpCodeProvider()) | ||
{ | ||
var typeRef = new CodeTypeReference(typeof(T)); | ||
typeName = provider.GetTypeOutput(typeRef); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice trick here! :) |
||
} | ||
} | ||
|
||
return "Mock<" + typeName + ":" + randomId + ">"; | ||
} | ||
|
||
private void CheckParameters() | ||
{ | ||
typeof(T).ThrowIfNotMockeable(); | ||
|
@@ -139,7 +161,16 @@ private void CheckParameters() | |
get { return (T)base.Object; } | ||
} | ||
|
||
internal override bool IsDelegateMock | ||
/// <include file='Mock.Generic.xdoc' path='docs/doc[@for="Mock{T}.Name"]/*'/> | ||
public string Name { get; set; } | ||
|
||
/// <include file='Mock.Generic.xdoc' path='docs/doc[@for="Mock{T}.ToString"]/*'/> | ||
public override string ToString() | ||
{ | ||
return this.Name; | ||
} | ||
|
||
internal override bool IsDelegateMock | ||
{ | ||
get { return typeof(T).IsDelegate(); } | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Reflection; | ||
using System.Text; | ||
using Castle.DynamicProxy; | ||
|
||
namespace Moq.Proxy | ||
{ | ||
/// <summary> | ||
/// 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. | ||
/// </summary> | ||
internal class ProxyMethodHook : AllMethodsHook | ||
{ | ||
/// <summary> | ||
/// Extends AllMethodsHook.ShouldInterceptMethod to also intercept Object.ToString(). | ||
/// </summary> | ||
public override bool ShouldInterceptMethod(Type type, MethodInfo methodInfo) | ||
{ | ||
var isObjectToString = methodInfo.DeclaringType == typeof(Object) && methodInfo.Name == "ToString"; | ||
return base.ShouldInterceptMethod(type, methodInfo) || isObjectToString; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// 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. | ||
/// </summary> | ||
internal abstract class ProxyBase | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we should call this InterfaceProxy instead? It's a bit more explicit. Not a fan of the "Base" suffix ;) |
||
{ | ||
public override string ToString() | ||
{ | ||
return ((IMocked)this).Mock.ToString() + ".Object"; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,6 @@ | ||
using System; | ||
using System.Collections; | ||
using System.Collections.Generic; | ||
using System.Linq.Expressions; | ||
using Xunit; | ||
|
||
|
@@ -16,6 +18,73 @@ public void CreatesMockAndExposesInterface() | |
Assert.NotNull(comparable); | ||
} | ||
|
||
[Fact] | ||
public void CanBeNamedForEasierDebugging() | ||
{ | ||
var mock = new Mock<IComparable>(); | ||
mock.Name = "my mock"; | ||
|
||
Assert.Equal("my mock", mock.ToString()); | ||
} | ||
|
||
[Fact] | ||
public void HasADefaultNameThatIsUnique() | ||
{ | ||
var mock = new Mock<IComparable>(); | ||
var mock2 = new Mock<IComparable>(); | ||
|
||
Assert.NotEqual(mock.ToString(), mock2.ToString()); | ||
} | ||
|
||
[Fact] | ||
public void HasADefaultNameThatIncludesItsTypeAndThatItsAMock() | ||
{ | ||
var mock = new Mock<IComparable>(); | ||
|
||
Assert.Contains("System.IComparable", mock.ToString()); | ||
Assert.Contains("mock", mock.ToString().ToLower()); | ||
} | ||
|
||
[Fact] | ||
public void HasADefaultNameThatIncludesItsGenericParameters() | ||
{ | ||
var mock = new Mock<Dictionary<int, string>>(); | ||
|
||
Assert.Contains("System.Collections.Generic.Dictionary<int, string>", mock.ToString()); | ||
} | ||
|
||
[Fact] | ||
public void PassesItsNameOnToTheResultingMockObjectWhenMockingInterfaces() | ||
{ | ||
var mock = new Mock<IComparable>(); | ||
|
||
Assert.Equal(mock.ToString() + ".Object", mock.Object.ToString()); | ||
} | ||
|
||
[Fact] | ||
public void PassesItsNameOnToTheResultingMockObjectWhenMockingClasses() | ||
{ | ||
var mock = new Mock<ArrayList>(); | ||
|
||
Assert.Equal(mock.ToString() + ".Object", mock.Object.ToString()); | ||
} | ||
|
||
public class ToStringOverrider | ||
{ | ||
public override string ToString() { | ||
return "real value"; | ||
} | ||
} | ||
|
||
[Fact] | ||
public void AllowsMockingAsNormalIfImplementationsHaveOverriddenToString() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like two tests mixed in one? |
||
var partialMock = new Mock<ToStringOverrider>() { CallBase = true }; | ||
var fullMock = new Mock<ToStringOverrider>(); | ||
|
||
Assert.Equal("real value", partialMock.Object.ToString()); | ||
Assert.Null(fullMock.Object.ToString()); | ||
} | ||
|
||
[Fact] | ||
public void ThrowsIfNullExpectAction() | ||
{ | ||
|
@@ -148,7 +217,7 @@ public void HashCodeIsDifferentForEachMock() | |
} | ||
|
||
[Fact] | ||
public void ToStringIsNullOrEmpty() | ||
public void ToStringIsNotNullOrEmpty() | ||
{ | ||
var mock = new Mock<IFoo>(); | ||
Assert.False(String.IsNullOrEmpty(mock.Object.ToString())); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does not look like an IMocked-related behavior. Should we create a separate HandleToString() strategy and revert the changes to the mixing one?