Moq implementation memo
Moq implementation memo
Note that I used Moq
in my C # test implementation.
environment
- Use Visual Studio 2019 project template (NUnit test project (.NET Core))
- Install
Moq
from NuGet package
Class implementation to be tested
It is an implementation for example only, and is not a test target for which Moq is effective.
Human.cs
public class Human
{
/// <summary>
///Last name
/// </summary>
private string FamilyName { get; }
/// <summary>
///name
/// </summary>
private string GivenName { get; }
/// <summary>
///age
/// </summary>
public virtual int Age { get; }
public Human(string familyName, string givenName, int age)
{
FamilyName = familyName;
GivenName = givenName;
Age = age;
}
/// <summary>
///Create a full name.
/// </summary>
/// <returns>full name</returns>
protected virtual string CreateFullName()
{
return $"{FamilyName} {GivenName}";
}
/// <summary>
///Create a full name with age.
/// </summary>
/// <returns>Full name with age</returns>
public virtual string CreateFullNameWithAge()
{
return $"{CreateFullName()} {Age}";
}
/// <summary>
///Create a full name with age and its units.
/// </summary>
/// <returns>Full name with age and its units</returns>
public virtual string CreateFullNameWithAge(string ageUnit)
{
return $"{CreateFullNameWithAge()}{ageUnit}";
}
}
important point
Points to note when implementing the test target class.
virual
The virtual
qualifier is required because the method to be mocked must be in an overridable state. Visibility is also protected
or better.
The following is an error statement when trying to mock a non-overridable method. (Temporarily remove the virtual
qualifier from the CreateFullName ()
method and execute)
System.NotSupportedException : Unsupported expression: mock => mock.CreateFullName()
Non-overridable members (here: Human.CreateFullName) may not be used in setup / verification expressions.
static
Static methods and properties cannot be mocked.
Test implementation
The structure of the class used in the test implementation item.
Tests.cs
public class Tests
{
/// <summary>
///Last name(Fixed value for testing)
/// </summary>
private string FamilyName { get; set; }
/// <summary>
///name(Fixed value for testing)
/// </summary>
private string GivenName { get; set; }
/// <summary>
///age(Fixed value for testing)
/// </summary>
private int Age { get; set; }
/// <summary>
///Common test settings.
/// </summary>
[SetUp]
public void Setup()
{
FamilyName = "Last name";
GivenName = "name";
Age = 20;
}
//Describe the test implementation below
}
Basic
Mocking with Moq uses the Moq.Mock <T>
class.
At the time of new
, set the class to be mocked in the <T>
part, and set the method of the constructor to be mocked in the argument.
Mocked objects are called from Moq # Object
.
var humanMock = new Mock<Human>(FamilyName, GivenName, Age)
humanMock.Object.CreateFullNameWithAge();
CallBase = false
The first thing I stumbled upon in the Mock
class was handling the CallBase
property.
This value is initially set to false
.
In the following example, CallBase
is false
, so the unmocked method will return null
. (The method itself is not called)
CallBase_False
[Test]
public void CallBase_False()
{
var humanMock = new Mock<Human>(FamilyName, GivenName, Age);
Assert.AreEqual(null, humanMock.Object.CreateFullNameWithAge());
}
CallBase = true
In the following example, CallBase
is true
, so the original method will be called for the unmocked method.
CallBase_True
[Test]
public void CallBase_True()
{
var humanMock = new Mock<Human>(FamilyName, GivenName, Age) { CallBase = true };
Assert.AreEqual($"{FamilyName} {GivenName} {Age}", humanMock.Object.CreateFullNameWithAge());
}
Other
Moq # Object
is the same class as the mock target. Not only can it be used directly in the test method, but it can also be used indirectly through arguments and fields.
Property mocking
Specify the property to be mocked from the Func
type argument of the Mock # SetupGet
method, and set the return value to be mocked from the Returns
method of the return object.
Mocked properties will always return the set return value.
OverrideProperty_Age
public void OverrideProperty_Age()
{
var humanMock = new Mock<Human>(FamilyName, GivenName, Age) { CallBase = true };
humanMock.SetupGet(m => m.Age).Returns(9999);
Assert.AreEqual($"{FamilyName} {GivenName} 9999", humanMock.Object.CreateFullNameWithAge());
Assert.AreEqual($"{FamilyName} {GivenName}9999 years old", humanMock.Object.CreateFullNameWithAge("Talent"));
}
Mocking public class methods
The argument of the mocked method of Setup (Func)
is only type specification
Specify the method to be mocked from the Func
type argument of the Mock # Setup
method, and set the return value to be mocked from the Returns
method of the return object.
Use the Moq.It <T>
class for the method set by the Func
type argument, and explicitly set the mocking method including the argument configuration.
OverrideMethod_CreateFullNameWithAgeUnit
/// <summary>
///Override CreateFullNameWithAge with arguments.
/// </summary>
[Test]
public void OverrideMethod_CreateFullNameWithAgeUnit()
{
var humanMock = new Mock<Human>(FamilyName, GivenName, Age) { CallBase = true };
humanMock.Setup(m => m.CreateFullNameWithAge(It.IsAny<string>())).Returns("Overwrite");
//The argument configuration specified in Setup is mocked
//The original method is not called and the return value set in Returns is returned
Assert.AreEqual("Overwrite", humanMock.Object.CreateFullNameWithAge("age"));
//Same name as the method specified in Setup, but with a different argument structure
//The original method is called because it is not subject to mocking
Assert.AreEqual($"{FamilyName} {GivenName} {Age}", humanMock.Object.CreateFullNameWithAge());
}
Fixed value for the argument of the mocked method of Setup (Func)
You can also set a fixed value without using Moq.It <T>
.
OverrideMethod_CreateFullNameWithAgeUnit_2
/// <summary>
///Override CreateFullNameWithAge with arguments.
/// </summary>
[Test]
public void OverrideMethod_CreateFullNameWithAgeUnit_2()
{
var humanMock = new Mock<Human>(FamilyName, GivenName, Age) { CallBase = true };
humanMock.Setup(m => m.CreateFullNameWithAge("age")).Returns("Overwrite");
//The method name and arguments specified in Setup match
//Due to mocking, the original method is not called and the return value set in Returns is returned.
Assert.AreEqual("Overwrite", humanMock.Object.CreateFullNameWithAge("age"));
//The argument configuration has the same name and type as the method specified in Setup, but the exact setting value is different.
//The original method is called because it is not subject to mocking
Assert.AreEqual($"{FamilyName} {GivenName} {Age}Talent", humanMock.Object.CreateFullNameWithAge("Talent"));
}
Other notes
- Asynchronous methods can use
ReturnsAsync
.
Mocking protected class methods
Mocking a method with no arguments
Set mocking from the Setup <T>
method of the return object of the Moq # Protected
method.
Set the return type in the <T>
part, and set the method name to be mocked in the first argument.
Mock_ProtectedMethod
/// <summary>
///Mocking protected methods.
/// </summary>
[Test]
public void Mock_ProtectedMethod()
{
var humanMock = new Mock<Human>(FamilyName, GivenName, Age) { CallBase = true };
humanMock.Protected().Setup<string>("CreateFullName").Returns("Overwrite");
//The overwritten return value is returned
Assert.AreEqual($"Overwrite{Age}", humanMock.Object.CreateFullNameWithAge());
}
Mocking methods with arguments
Set the argument configuration using ʻItExpr.IsAny
MockProtectedMethod_WithArgs
humanMock.Protected().Setup<string>("GetAgeWithUnit", ItExpr.IsAny<string>()).Returns("Overwrite");
Method execution
I think it’s unusual for a normal test implementation to not always test a mocked method.
It also describes how to execute reflection for methods that are not in the range of Moq
but whose visibility is not public
.
Run_ProtectedMethod
[Test]
public void Run_ProtectedMethod()
{
var human = new Human(FamilyName, GivenName, Age);
Type type = human.GetType();
MethodInfo methodInfo = type.GetMethod("CreateFullName", BindingFlags.Instance | BindingFlags.NonPublic);
//For methods with arguments, the second argument is object[]Pass a value of type
Assert.AreEqual($"{FamilyName} {GivenName}", methodInfo.Invoke(human, null));
}
Mocking interface methods
The above is the description of the class method.
I felt uncomfortable about making the method overridable just for testing, but @ naminodarie’s comment showed that the interface could be used to avoid overriding.
IHuman.cs
/// <summary>
///The interface of the class under test.
/// </summary>
public interface IHuman
{
/// <summary>
///Create a full name with age.
/// </summary>
/// <returns>Full name with age</returns>
string CreateFullNameWithAge();
/// <summary>
///Create a full name with age and its units.
/// </summary>
/// <returns>Full name with age and its units</returns>
string CreateFullNameWithAge(string ageUnit);
}
Mock_Interface
[Test]
public void Mock_Interface()
{
var humanMock = new Mock<IHuman>();
//Mocking settings are the same as based on the class
humanMock.Setup(m => m.CreateFullNameWithAge(It.IsAny<string>())).Returns("Overwrite");
//Since it is the same as the argument setting specified in Setup, it is not called and the return value set in Returns is returned.
Assert.AreEqual("Overwrite", humanMock.Object.CreateFullNameWithAge("age"));
}
Premonition that it seems to be particularly effective when temporarily implementing or using fake objects in a specific environment.
Other notes
However, it cannot be used for the following purposes.
- Mocking a method called from a method.
- Mock protected methods.
Miscellaneous feelings
- Convenient.
- Is the class actually used for mocking
Mock
? - What is the appropriate way to test around the process using static methods? Should static methods be used in a dedicated method instead of being described in the middle of the method?