Basit Bir Mock Framework Geliştirelim 3
2023-05-11
Bu sefer belirli parametrelerle çağrılan metotları simüle edeceğiz ve geri dönüş değerini ayarlayabileceğiz. Ayrıca, belirli bir metot çağrısını doğrulayabileceğiz.
using System;
using System.Collections.Concurrent;
using System.Linq.Expressions;
using System.Reflection;
public class Mock<T> : DispatchProxy
{
private ConcurrentDictionary<string, object> _methodResponses = new();
private ConcurrentDictionary<string, int> _methodCalls = new();
protected override object Invoke(MethodInfo targetMethod, object[] args)
{
var key = GetKey(targetMethod, args);
if (_methodResponses.TryGetValue(key, out var value))
{
if (_methodCalls.ContainsKey(key))
{
_methodCalls[key]++;
}
else
{
_methodCalls.TryAdd(key, 1);
}
return value;
}
throw new Exception($"No response set up for method {targetMethod.Name}");
}
private string GetKey(MethodInfo methodInfo, object[] args)
{
return $"{methodInfo.Name}_{string.Join("_", args)}";
}
public void Setup(Expression<Action<T>> expression, object response)
{
var methodCall = expression.Body as MethodCallExpression;
var methodName = methodCall.Method.Name;
var arguments = new object[methodCall.Arguments.Count];
for (int i = 0; i < methodCall.Arguments.Count; i++)
{
var arg = methodCall.Arguments[i];
if (arg.NodeType == ExpressionType.Constant)
{
arguments[i] = ((ConstantExpression)arg).Value;
}
}
var key = GetKey(methodCall.Method, arguments);
_methodResponses.TryAdd(key, response);
}
public void Verify(Expression<Action<T>> expression, int expectedInvocationCount = 1)
{
var methodCall = expression.Body as MethodCallExpression;
var methodName = methodCall.Method.Name;
var arguments = new object[methodCall.Arguments.Count];
for (int i = 0; i < methodCall.Arguments.Count; i++)
{
var arg = methodCall.Arguments[i];
if (arg.NodeType == ExpressionType.Constant)
{
arguments[i] = ((ConstantExpression)arg).Value;
}
}
var key = GetKey(methodCall.Method, arguments);
if (_methodCalls.TryGetValue(key, out var actualInvocationCount))
{
if (actualInvocationCount != expectedInvocationCount)
{
throw new Exception($"Method {methodName} was expected to be called {expectedInvocationCount} times but was called {actualInvocationCount} times.");
}
}
else
{
throw new Exception($"Method {methodName} was expected to be called {expectedInvocationCount} times but was not called.");
}
}
public static T Create()
{
return Create<T, Mock<T>>();
}
}
Bu yeni mock sınıfını şu şekilde kullanabiliriz:
public interface ITestInterface
{
string TestMethod(int parameter);
}
public class Test
{
public void TestMethod()
{
var mock = Mock<ITestInterface>.Create();
(mock as Mock<ITestInterface>).Setup(x => x.TestMethod(5), "Test response for 5");
var result = mock.TestMethod(5);
Console.WriteLine(result); // Outputs: Test response for 5
(mock as Mock<ITestInterface>).Verify(x => x.TestMethod(5)); // Throws no exception
}
}
Bu örnekte, ITestInterface
arayüzünün TestMethod
metodunu mockluyoruz. TestMethod
çağrıldığında, parametresi 5 olduğunda “Test response for 5” döndürülür.
Ayrıca, Verify
metodu ile TestMethod
metodunun belirli bir parametre ile çağrılıp çağrılmadığını kontrol ediyoruz. Bu durumda, TestMethod(5)
çağrısını kontrol ediyoruz. Eğer bu çağrı yapılmamış olsaydı, bir hata fırlatılırdı.