Arrange All Instances of a Class
Telerik® JustMock enables you to create a single arrangement and apply it to each class instance no matter where and when it is being created in the current context. With this feature, you don't need to arrange every instance explicitly. You will find this handy, especially in cases when you have to mock third party controls and tools where you have little control over how they are created.
This is an elevated feature. Refer to this topic to learn more about the differences between the commercial and the free versions of Telerik JustMock.
By default, all mocks are tied to an instance and this is the expected behavior. This topic describes the approaches you can use to change that behavior and apply the arrangements to all instances of a type.
Mock the Creation of a New Class Object
Creation of a new class instance in the code under test is typically a great challenge when unit testing. With JustMock you are able to easily mock the constructor of an instance so that you can apply a specific behavior to it or alter its return value. Let`s assume we have the following class:
Sample setup
public class FooWithNotImplementedConstructor
{
public FooWithNotImplementedConstructor()
{
throw new NotImplementedException();
}
}
Public Class FooWithNotImplementedConstructor
Public Sub New()
Throw New NotImplementedException()
End Sub
End Class
We can easily arrange its constructor like shown in Example 1.
Example 1: Arrange the behavior of the constructor of an object
[TestMethod]
public void ShouldMockConstructorForFutureInstances()
{
// Arrange
Mock.Arrange(() => new FooWithNotImplementedConstructor()).DoNothing(); // Directly arranging the constructor
// Act
var myNewInstance = new FooWithNotImplementedConstructor(); // This will not throw an exception
// Assert
Assert.IsNotNull(myNewInstance);
Assert.IsInstanceOfType(myNewInstance, typeof(FooWithNotImplementedConstructor));
}
<TestMethod>
Public Sub ShouldMockConstructorForFutureInstances()
' Arrange
Mock.Arrange(Function() New FooWithNotImplementedConstructor()).DoNothing() ' Directly arranging the constructor
' Act
Dim myNewInstance = New FooWithNotImplementedConstructor() ' This will not throw an exception
' Assert
Assert.IsNotNull(myNewInstance)
Assert.IsInstanceOfType(myNewInstance, GetType(FooWithNotImplementedConstructor))
End Sub
This will apply DoNothing to the constructor of each new instance of type FooWithNotImplementedConstructor
called during the test method.
Arrange Return Value for Constructor
You can arrange a return value for a new object creation. Let's assume we have the following class:
Sample setup
public class FooWithProp
{
public string MyProp { get; set; }
}
public FooWithProp GetNewInstance()
{
return new FooWithProp();
}
Public Class FooWithProp
Public Property MyProp As String
End Class
Public Function GetNewInstance() As FooWithProp
Return New FooWithProp()
End Function
You can easily arrange each new instance of the FooWithProp
class, to return a predefined object of the same type:
Example 2: Return a predefined object when constructor is invoked
[TestMethod]
public void ShouldReturnNewObjectForFutureInstances()
{
// Arrange
var testObj = new FooWithProp() { MyProp = "Test" };
// Directly arranging the expression to return our predefined object
Mock.Arrange(() => new FooWithProp()).Returns(testObj);
// Act
var myNewInstance = GetNewInstance();
// Assert
Assert.IsNotNull(myNewInstance);
Assert.IsInstanceOfType(myNewInstance, typeof(FooWithProp));
// Assert that the returned instance is equal to the predefined
Assert.AreEqual("Test", myNewInstance.MyProp);
}
<TestMethod>
Public Sub ShouldReturnNewObjectForFutureInstances()
' Arrange
Dim testObj = New FooWithProp()
testObj.MyProp = "Test"
' Directly arranging the expression to return our predefined object
Mock.Arrange(Function() New FooWithProp()).Returns(testObj)
' Act
Dim myNewInstance = GetNewInstance()
' Assert
Assert.IsNotNull(myNewInstance)
Assert.IsInstanceOfType(myNewInstance, GetType(FooWithProp))
' Assert that the returned instance is equal to the predefined
Assert.AreEqual("Test", myNewInstance.MyProp)
End Sub
This will work for each new instance of the FooWithProp
type outside the test method. Still, it applies only for code, executed under the test context.
Ignore Instance for an Expectation
The instance that is created in Example 3 is an object of type UserData
, which has a single method called ReturnFive()
which returns an integer. Then we mock the call to the ReturnFive()
method for the mocked instance(userDataMock
) of the UserData
class. With this setup, the ReturnFive()
method will not be mocked for any of the other instances of the UserData
class.
Sample setup
public class UserData
{
public int ReturnFive()
{
return 5;
}
}
Public Class UserData
Public Function ReturnFive() As Integer
Return 5
End Function
End Class
Example 3: Arrange method for specific instance
[TestMethod]
public void ShouldArrangeReturnOnlyForSpecificInstance()
{
// Arrange
var userDataMock = Mock.Create<UserData>();
Mock.Arrange(() => userDataMock.ReturnFive()).Returns(7);
// Assert
Assert.AreEqual(7, userDataMock.ReturnFive());
Assert.AreEqual(5, new UserData().ReturnFive());
}
<TestMethod>
Public Sub ShouldArrangeReturnOnlyForSpecificInstance()
' Arrange
Dim userDataMock = Mock.Create(Of UserData)()
Mock.Arrange(Function() userDataMock.ReturnFive()).Returns(7)
' Assert
Assert.AreEqual(7, userDataMock.ReturnFive())
Assert.AreEqual(5, New UserData().ReturnFive())
End Sub
If you need to apply future mocking and assure that all UserData
instances use the same mocked version of the ReturnFive()
method, you can call IgnoreInstance()
.
Example 4: Arrange method for all object instances
[TestMethod]
public void ShouldArrangeReturnForFutureUserDataInstances()
{
// Arrange
var userDataMock = Mock.Create<UserData>();
Mock.Arrange(() => userDataMock.ReturnFive()).IgnoreInstance().Returns(7);
// Assert
Assert.AreEqual(7, userDataMock.ReturnFive());
Assert.AreEqual(7, new UserData().ReturnFive());
}
<TestMethod>
Public Sub ShouldArrangeReturnForFutureUserDataInstances()
' Arrange
Dim userDataMock = Mock.Create(Of UserData)()
Mock.Arrange(Function() userDataMock.ReturnFive()).IgnoreInstance().Returns(7)
' Assert
Assert.AreEqual(7, userDataMock.ReturnFive())
Assert.AreEqual(7, New UserData().ReturnFive())
End Sub
When IgnoreInstance
is applied, the ReturnFive()
method will work according to the expectation you have set in Mock.Arrange
.
When setting future expectations, you can also use the Fluent Mocking API as in this example:
Example 5: Arrange method for all object instances using fluent syntax
[TestMethod]
public void ShouldArrangeReturnForFutureUserDataInstances_Fluent()
{
// Arrange
var userDataMock = Mock.Create<UserData>();
userDataMock.Arrange(mock => mock.ReturnFive()).IgnoreInstance().Returns(7);
// Assert
Assert.AreEqual(7, userDataMock.ReturnFive());
Assert.AreEqual(7, new UserData().ReturnFive());
}
<TestMethod>
Public Sub ShouldArrangeReturnForFutureUserDataInstances_Fluent()
' Arrange
Dim userDataMock = Mock.Create(Of UserData)()
userDataMock.Arrange(Function(x) x.ReturnFive()).IgnoreInstance().Returns(7)
' Assert
Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(7, userDataMock.ReturnFive())
Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(7, New UserData().ReturnFive())
End Sub
Using IgnoreInstance for Virtual Methods
You can also apply IgnoreInstance
to virtual methods. Imagine that we have a simple class Calculator
which has a virtual
method Sum
:
Sample setup
public class Calculator
{
public virtual int Sum()
{
return 0;
}
}
Public Class Calculator
Public Overridable Function Sum() As Integer
Return 0
End Function
End Class
Now we can use IgnoreInctance
in the exact same way:
Example 6: Ignore instance for virtual method
[TestMethod]
public void ShouldApplyIgnoreInstanceToVirtual()
{
//Arrange
var calculator = Mock.Create<Calculator>();
Mock.Arrange(() => calculator.Sum()).IgnoreInstance().Returns(10);
//Assert
Assert.AreEqual(10, calculator.Sum());
Assert.AreEqual(10, new Calculator().Sum());
}
<TestMethod>
Public Sub ShouldApplyIgnoreInstanceToVirtual()
'Arrange
Dim calculator = Mock.Create(Of Calculator)()
Mock.Arrange(Function() calculator.Sum()).IgnoreInstance().Returns(10)
'Assert
Assert.AreEqual(10, calculator.Sum())
Assert.AreEqual(10, New Calculator().Sum())
End Sub
Ignore Instance for Collections
This section shows how you can arrange a property to return a fake collection for each instance of the object. To demonstrate this, we will use the Foo
class below.
Sample setup
public class Foo
{
public Foo()
{
}
private List<object> collection;
public List<object> RealCollection
{
get { return collection; }
}
}
Public Class Foo
Public Sub New()
End Sub
Private collection As List(Of Object)
Public ReadOnly Property RealCollection() As List(Of Object)
Get
Return collection
End Get
End Property
End Class
Example 7 shows the creation of the fake collection that will be used in the test and how you can arrange the Foo.RealCollection
property to always return that fake collection.
Example 7: Construct fake collection and arrange property get to return it
public IList<object> FakeCollection()
{
List<object> resultCollection = new List<object>();
resultCollection.Add("asd");
resultCollection.Add(123);
resultCollection.Add(true);
return resultCollection;
}
[TestMethod]
public void ShouldReturnFakeCollectionForFutureCall()
{
var fooMocked = Mock.Create<Foo>();
var expectedCollection = FakeCollection();
// Arrange
Mock.Arrange(() => fooMocked.RealCollection).IgnoreInstance().ReturnsCollection(expectedCollection);
// Act
var actualArrangedCollection = fooMocked.RealCollection;
var actualUnArrangedCollection = new Foo().RealCollection;
// Assert
// Asserting for the arranged instance
Assert.AreEqual(expectedCollection.Count, actualArrangedCollection.Count);
Assert.AreEqual(expectedCollection.FirstOrDefault(), actualArrangedCollection.FirstOrDefault());
// Asserting for a new unarranged instance
Assert.AreEqual(expectedCollection.Count, actualUnArrangedCollection.Count);
Assert.AreEqual(expectedCollection.FirstOrDefault(), actualUnArrangedCollection.FirstOrDefault());
}
Public Function FakeCollection() As IList(Of Object)
Dim resultCollection As New List(Of Object)()
resultCollection.Add("asd")
resultCollection.Add(123)
resultCollection.Add(True)
Return resultCollection
End Function
<TestMethod>
Public Sub ShouldReturnFakeCollectionForFutureCall()
Dim fooMocked = Mock.Create(Of Foo)()
Dim expectedCollection = FakeCollection()
' Arrange
Mock.Arrange(Function() fooMocked.RealCollection).IgnoreInstance().ReturnsCollection(expectedCollection)
' Act
Dim actualArrangedCollection = fooMocked.RealCollection
Dim actualUnArrangedCollection = New Foo().RealCollection
' Assert
' Asserting for the arranged instance
Assert.AreEqual(expectedCollection.Count, actualArrangedCollection.Count)
Assert.AreEqual(expectedCollection.FirstOrDefault(), actualArrangedCollection.FirstOrDefault())
' Asserting for a new unarranged instance
Assert.AreEqual(expectedCollection.Count, actualUnArrangedCollection.Count)
Assert.AreEqual(expectedCollection.FirstOrDefault(), actualUnArrangedCollection.FirstOrDefault())
End Sub
You can see that the test logic is the same as in the previous tests, with the only difference that a collection is being returned this time.
Mocking All Instances Across Threads
Mocking across all threads is an unsafe operation because it may compromise the stability of the testing framework. Arrangements that ignore the instance are valid only for the current thread by default. To make an arrangement that ignores the instance and it is valid on all threads, add the .OnAllThreads()
clause to the arrangement:
Example 8: Using OnAllThreads
Mock.Arrange(() => new DBContext()).DoNothing().OnAllThreads();
Mock.Arrange(Function() New DBContext()).DoNothing().OnAllThreads()