chevron-thin-right chevron-thin-left brand cancel-circle search youtube-icon google-plus-icon linkedin-icon facebook-icon twitter-icon toolbox download check linkedin phone twitter-old google-plus facebook profile-male chat calendar profile-male
0 votes

It surprised me to learn today that calling Isolate.Swap.NextInstance<T>().With(reference) then calling new T() does not result in new T() returning that exact reference:

Is this behavior by design?

Code from screenshot:

using System;
using NUnit.Framework;
using TypeMock.ArrangeActAssert;

public class Class
{
	public static Class New()
	{
		return new Class();
	}
}

[TestFixture, Isolated]
public class Tests
{
	[Test]
	public void TestWhetherFakeClassReturnedFromStaticMethodIsEqualToItself()
	{
		var fakeClass = Isolate.Fake.Instance<Class>();
		Isolate.WhenCalled(() => Class.New()).WillReturn(fakeClass);
		//
		var newClass = Class.New();
		//
		Assert.IsTrue(Object.ReferenceEquals(fakeClass, newClass)); // Succeeds
	}

	[Test]
	public void TestWhetherFakeClassReturnedThroughSwapNextInstanceIsEqualToItself()
	{
		var fakeClass = Isolate.Fake.Instance<Class>();
		Isolate.Swap.NextInstance<Class>().With(fakeClass);
		//
		var newClass = new Class(); 
		//
		Assert.IsTrue(Object.ReferenceEquals(fakeClass, newClass)); // Fails. By design?
	}
}
asked by NeilJustice (14.1k points)
edited by NeilJustice

1 Answer

0 votes

Hi Neil,

Isolate.Swap.NextInstance is obsolete.

You can use Fake.NextInstance or Fake.AllInstances instead.

Please see our documentation for NextInstance and AllInstances.

answered by Bar (3.3k points)

Hi Bar,

I'm seeing the same behavior of an unexpected reference being returned from new T() after calling either Isolate.Fake.NextInstance or Isolate.Fake.AllInstances:

using System;
using NUnit.Framework;
using TypeMock.ArrangeActAssert;

public class Class
{
	public static Class New()
	{
		return new Class();
	}
}

[TestFixture, Isolated]
public class Tests
{
	[Test]
	public void FakeNextInstance()
	{
		var fakeNextInstanceOfClass = Isolate.Fake.NextInstance<Class>();
		//
		var nextInstanceOfClass = new Class();
		//
		Assert.IsTrue(Object.ReferenceEquals(fakeNextInstanceOfClass, nextInstanceOfClass));
	}

	[Test]
	public void FakeAllInstances()
	{
		var fakeNextInstanceOfClass = Isolate.Fake.AllInstances<Class>();
		//
		var nextInstanceOfClass = new Class();
		//
		Assert.IsTrue(Object.ReferenceEquals(fakeNextInstanceOfClass, nextInstanceOfClass));
	}
}

 

What you get from the fake.next\all is a proxy, not the actuall instance.

Basically the method calls are redirected from the proxy (the instance that you set fakes on )to your real instance.

Do you need a handle to the real future instance in your tests?

Send us an example and we'll try to help.

Hi Bar,

Thanks for that interesting note about the proxy.

Check out this scenario whereby a handle to the real future instance being nowhere in sight leads to a tragic test failure:

As text:

using System;
using NUnit.Framework;
using TypeMock.ArrangeActAssert;

public class ClassA
{
	public ClassB _classB = new ClassB();

	public void ClassAMethod()
	{
		var newClassC = new ClassC();
		_classB.ClassBMethod(newClassC);
	}
}

public class ClassB
{
	public void ClassBMethod(ClassC classC) {}
}

public class ClassC
{
	// Imagine that for whatever reason the user
	// has implemented Equals() in terms of reference equality.
	public override bool Equals(object obj) 
	{ 
		bool areEqual = Object.ReferenceEquals(this, obj); 
		Console.WriteLine("Object.ReferenceEquals(this, obj): " + areEqual); 
		return areEqual;
	}

	public override int GetHashCode() { throw new NotImplementedException(); }
}

[TestFixture, Isolated]
public class Tests
{
	[Test]
	public void ClassAMethod_CallsClassBMethodWithNewClassC()
	{
		var classA = new ClassA();

		var fakeClassB = Isolate.Fake.Instance<ClassB>(Members.MustBeSpecified);
		Isolate.WhenCalled(() => fakeClassB.ClassBMethod(null)).IgnoreCall();
		classA._classB = fakeClassB;

		var anObjectOfTypeCWhichIsUsedAsAMockingHandle 
			= Isolate.Fake.NextInstance<ClassC>(Members.MustBeSpecified);
		Isolate.WhenCalled(() => 
			anObjectOfTypeCWhichIsUsedAsAMockingHandle.Equals(null)).CallOriginal();
		//
		classA.ClassAMethod();
		//
		Isolate.Verify.WasCalledWithExactArguments(() => 
			fakeClassB.ClassBMethod(anObjectOfTypeCWhichIsUsedAsAMockingHandle));
	}
}

 

Here is a real life example from the code I am working on whereby a unit test is not able to include an assertion regarding the reference equality of a return value, because absent is a reference to the real future instance of T following calling Isolate.Fake.NextInstance<T> then new T():

The workaround I have been using to ensure my return values are fully tested is to write the less readable but return value settable statement "T.New<T>()" instead of the more readable but not return value settable statement "new T()":

Hi Neil,

How about the following API:

 Isolate.Verify.GetInstancesOf(fake)[1];

This will collect instances of future fakes - so you can grab them and use them for verification, although you will not be able to IsolateVerify them only the original fakes.

Also, could you publish your Isolator2 apis' they look cool

Hi Eli,

That API for getting future fakes sounds great.

Here is Isolate2.cs:

using System;
using System.Diagnostics;
using NUnit.Framework;
using TypeMock.ArrangeActAssert;

[DebuggerStepThrough]
public static class Isolate2
{
	public static T Fake<T>(Members members = Members.MustBeSpecified)
	{
		return Isolate.Fake.Instance<T>(members);
	}

	public static T FakeNext<T>()
	{
		return Isolate.Fake.NextInstance<T>(Members.MustBeSpecified);
	}

	public static T ReturnFakeFromGetter<T>(Func<T> getter)
	{
		var fakeT = Isolate2.Fake<T>();
		Isolate2.Return(fakeT, getter);
		return fakeT;
	}

	public static void ReturnIf<T>(bool doSetReturnValue, T returnValue, Func<T> func)
	{
		if (doSetReturnValue)
		{
			Isolate.WhenCalled(func).WillReturn(returnValue);
		}
	}

	public static void FakeStatics(Type type)
	{
		Isolate.Fake.StaticMethods(type, Members.MustBeSpecified);
	}

	public static void Return<T>(T returnValue, Func<T> func)
	{
		Isolate.WhenCalled(func).WillReturn(returnValue);
	}

	public static void Ignore(Action action)
	{
		Isolate.WhenCalled(action).IgnoreCall();
	}

	public static void Ignore<T>(Func<T> action)
	{
		throw new NotImplementedException("Cannot Ignore() a non-void method");
	}

	public static void ExactArgs(Action action)
	{
		Isolate.Verify.WasCalledWithExactArguments(action);
	}

	public static void ExactArgs<T>(Func<T> func)
	{
		Isolate.Verify.WasCalledWithExactArguments(func);
	}

	public static void ExactArgsOnce(Action action)
	{
		Isolate2.ExactArgsNTimes(1, action);
	}

	public static void ExactArgsOnce<T>(Func<T> func)
	{
		Isolate2.ExactArgsNTimes(1, func);
	}

	public static void ExactArgsOnceIf(bool doAssertExactArgsOnce, Action action)
	{
		if (doAssertExactArgsOnce)
		{
			Isolate2.ExactArgsOnce(action);
		}
		else
		{
			Isolate2.NotCalled(action);
		}
	}

	public static void ExactArgsNTimes(int n, Action action)
	{
		if (n > 0)
		{
			Isolate.Verify.WasCalledWithExactArguments(action);
		}
		Assert.AreEqual(n, Isolate2.GetTimesCalled(action), "Times called");
	}

	public static void ExactArgsNTimes<T>(int n, Func<T> func)
	{
		if (n > 0)
		{
			Isolate.Verify.WasCalledWithExactArguments(func);
		}
		Assert.AreEqual(n, Isolate2.GetTimesCalled(func), "Times called");
	}

	public static int GetTimesCalled(Action action)
	{
		return Isolate.Verify.GetTimesCalled(action);
   }

	public static int GetTimesCalled<T>(Func<T> func)
	{
		return Isolate.Verify.GetTimesCalled(func);
	}

	public static void NotCalled(Action action)
	{
		Isolate.Verify.WasNotCalled(action);
	}

	public static void Throw(Exception exception, Action action)
	{
		Isolate.WhenCalled(action).WillThrow(exception);
	}

	public static void ThrowIf(bool doThrow, Exception exception, Action action)
	{
		if (doThrow)
		{
			Isolate.WhenCalled(action).WillThrow(exception);
		}
		else
		{
			Isolate.WhenCalled(action).IgnoreCall();
		}
	}

	public static void TimesCalled(int expectedTimesCalled, Action action)
	{
		Assert.AreEqual(expectedTimesCalled, Isolate2.GetTimesCalled(action));
	}

	public static void TimesCalled<T>(int expectedTimesCalled, Func<T> func)
	{
		Assert.AreEqual(expectedTimesCalled, Isolate2.GetTimesCalled(func));
	}

	public static void CallOriginal(Action action)
	{
		Isolate.WhenCalled(action).CallOriginal();
	}
}

 

Hi Neil,

Thanks for sharing your Typemock API :)

We added the API that ELI showed you.

You can download the it here.

Let us know if it works fine.

 

Thanks Alex and Eli. Isolate.Verify.GetInstancesOf() works as expected!

using NUnit.Framework;
using TypeMock.ArrangeActAssert;

public class C
{
}

[TestFixture, Isolated]
public class GetInstancesOfTests
{
	[Test]
	public void WorksForNextInstance()
	{
		var nextInstanceProxy = Isolate.Fake.NextInstance<C>();
		var nextInstanceOfC = new C();
		//			
		C[] newedFakeInstancesOfC = Isolate.Verify.GetInstancesOf(nextInstanceProxy);
		//
		Assert.AreEqual(new C[] { nextInstanceOfC }, newedFakeInstancesOfC);
	}

	[Test]
	public void WorksForAllInstances()
	{
		var nextInstanceProxy = Isolate.Fake.AllInstances<C>();
		var firstInstanceOfC = new C();
		var secondInstanceOfC = new C();
		//			
		C[] newedFakeInstancesOfC = Isolate.Verify.GetInstancesOf(nextInstanceProxy);
		//
		Assert.AreEqual(new C[] { firstInstanceOfC, secondInstanceOfC }, newedFakeInstancesOfC);
	}
}

 

Great,

It'll be officialy available as of next release (8.2.1)
...