C# Unit test moq delegate
▌Introduction
This
article shows how to mock delegate for unit-testing.
▋Related articles
▌Environment
▋Visual Studio 2017 community
▋dotnet core 2.2.101
▋Moq 4.10.1
▌Implement
We
will test the Get(…) function of class, DataAccessService<T> which
implements interface, IDataAccessService<T>. It has a delegate, AuthorizeEvent, which we
will mock in this test.
The
unit test’s goal:
1. Test Get(…) function’s accuracy
2. The delegate is called
▋Interfaces and Classes
▋IDataAccessService.cs
public delegate bool Authorize(string caller);
public interface
IDataAccessService<T>
{
string
ConnectionStr { get; set; }
event Authorize
AuthorizeEvent;
T Get(string id);
}
▋DataAccessService.cs
public class
DataAccessService<T> : IDataAccessService<T> where T:new()
{
private readonly string _caller = "DataAccessService";
public string
ConnectionStr { get; set; }
public event Authorize
AuthorizeEvent;
public
DataAccessService()
{
}
public T Get(string id)
{
//HACK:
Implement the logic here to replace the below line
if (this.AuthorizeEvent(this._caller) ==
true)
return new T();
else
return default(T);
}
}
▋Unit test without mocking the delegate
Lets see a bad idea of not mocking the delegate in
the unit test.
That means we have to create a method as a reference for the delegate, and a static flag
to make sure the delegate is triggered.
private static bool
callbackIsTriggered = false;
[Test]
public void TestGet()
{
IDataAccessService<Birthday>
dataAccess = new DataAccessService<Birthday>();
#region
Use the real method
dataAccess.AuthorizeEvent += MySaveLogAction;
var rtn =
dataAccess.Get(It.IsAny<string>());
Assert.IsTrue(callbackIsTriggered);
Assert.IsFalse(rtn == null);
#endregion
}
private static bool
MySaveLogAction(string caller)
{
Debug.WriteLine($"Called from {caller}");
callbackIsTriggered = true;
return true;
}
}
▋Unit test by mocking delegate with moq
We will use moq to mock the delegate, so that we can
focus on tesing the IDataAccessService.Get() function.
[Test]
public void TestGet()
{
IDataAccessService<Birthday>
dataAccess = new DataAccessService<Birthday>();
#region
Mock the callback
var
mockCallback = new Mock<Authorize>();
mockCallback.Setup(x =>
x(It.IsAny<string>())).Returns(true);
dataAccess.AuthorizeEvent += mockCallback.Object;
dataAccess.Get(It.IsAny<string>());
mockCallback.Verify(x => x(It.IsAny<string>()),
Times.Once);
Assert.IsFalse(rtn
== null);
#endregion
}
The above code mocks the delegate: Authorize,
and setup the returning value. (If the delegate returns nothing, then ignore .Returns(…))
in this line:
mockCallback.Setup(x => x(It.IsAny<string>())).Returns(true);
And notice we CANNOT directly mock the Invoke method of a delegate like this:
mockCallback.Setup(x => x.Invoke(It.IsAny<string>())).Returns(true);
Which will cause the error:
System.InvalidCastException : Unable to
cast object of type 'System.Linq.Expressions.InstanceMethodCallExpression1' to
type 'System.Linq.Expressions.InvocationExpression'.
▌Reference
沒有留言:
張貼留言