I have discovered what appears to be a pretty serious defect in the new AAA API's.. if you try to mock a class that overrides Object.GetHashCode() it will almost always blow up w/ a NullReferenceException.
Here is some repro code:
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TypeMock.ArrangeActAssert;
namespace TypeMock.HashCodeBug
{
[TestClass]
public sealed class Test
{
private sealed class ClassWithHashCode
{
private string _test;
public ClassWithHashCode()
{
_test = "";
}
public override int GetHashCode()
{
return _test.GetHashCode();
}
}
[TestMethod]
public void TestNormal()
{
ClassWithHashCode instance = new ClassWithHashCode();
instance.GetHashCode();
}
[TestMethod]
public void TestMocked()
{
ClassWithHashCode instance = Isolate.Fake.Instance<ClassWithHashCode>();
}
}
}
Also, here is the exception:
System.NullReferenceException: Object reference not set to an instance of an object.
at TypeMock.HashCodeBug.Test.ClassWithHashCode.GetHashCode() in C:LocalResearchTypeMock.HashCodeBugTypeMock.HashCodeBugTest.cs:line 21
at System.Collections.Generic.ObjectEqualityComparer`1.GetHashCode(T obj)
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
at y.a(Object A_0, Object[] A_1, Object A_2)
at a2.a(MethodBase A_0, Object A_1, Object[] A_2, Object A_3)
at TypeMock.MockManager.a(String A_0, String A_1, Object A_2, Object A_3, Boolean A_4, Object[] A_5)
at TypeMock.InternalMockManager.getReturn(Object that, String typeName, String methodName, Object methodParameters, Boolean isInjected)
at TypeMock.HashCodeBug.Test.ClassWithHashCode..ctor() in C:LocalResearchTypeMock.HashCodeBugTypeMock.HashCodeBugTest.cs:line 14
at TypeMock.MockManager.a(Type A_0, Object[] A_1)
at TypeMock.MockManager.MockObject(Type type, Constructor mockConstructors, Object[] args)
at TypeMock.MockManager.MockObject[TMockedType](Constructor mockConstructors)
at ar.StubVoidCalls[T]()
at ar.Instance[T](Members behavior)
at ar.Instance[T]()
at TypeMock.HashCodeBug.Test.TestMocked() in C:LocalResearchTypeMock.HashCodeBugTypeMock.HashCodeBugTest.cs:line 37
Based on what I have seen it seems that TypeMock calls the real GetHashCode method on mocked classes when it instantiates them. The reason this specific code blows up is because it (rightly) expects _test to not be null, and calls GetHashCode on it. You will note that the TestNormal unit test does not fail.
Since all the fields will be null in the mocked object ClassWithHashCode.GetHashCode() blows on that statement.
I assume you guys are using the object hashcodes internally to store mocked objects in a hash table, but calling GetHashCode on the newly instantiated mocked object is incorrect for the reasons outlined above. Not to mention that this would be incorrect anyways because the "real" behavior of GetHashCode is undefined from your point of view.
This is a critical bug for us, we recently bought Typemock Isolator 5.0 but we can't use it with practically any of the classes we plan on mocking because they all override GetHashCode.
Thanks,
Joe Crivello