EDIT : Dalam Moq 4.10, Anda sekarang dapat melewati delegasi yang memiliki parameter keluar atau ref langsung ke fungsi Callback:
mock
.Setup(x=>x.Method(out d))
.Callback(myDelegate)
.Returns(...);
Anda harus mendefinisikan delegasi dan membuat instantiate:
...
.Callback(new MyDelegate((out decimal v)=>v=12m))
...
Untuk versi Moq sebelum 4.10:
Avner Kashtan menyediakan metode ekstensi di blog-nya yang memungkinkan pengaturan parameter keluar dari callback: Parameter Moq, Callbacks dan Out: kasus tepi yang sangat rumit
Solusinya elegan dan hacky. Elegan karena memberikan sintaks yang lancar yang terasa di rumah dengan panggilan balik Moq lainnya. Dan hacky karena mengandalkan memanggil beberapa API Moq internal melalui refleksi.
Metode ekstensi yang disediakan di tautan di atas tidak dapat dikompilasi untuk saya, jadi saya telah menyediakan versi yang diedit di bawah. Anda harus membuat tanda tangan untuk setiap jumlah parameter input yang Anda miliki; Saya telah memberikan 0 dan 1, tetapi memperluasnya harus sederhana:
public static class MoqExtensions
{
public delegate void OutAction<TOut>(out TOut outVal);
public delegate void OutAction<in T1,TOut>(T1 arg1, out TOut outVal);
public static IReturnsThrows<TMock, TReturn> OutCallback<TMock, TReturn, TOut>(this ICallback<TMock, TReturn> mock, OutAction<TOut> action)
where TMock : class
{
return OutCallbackInternal(mock, action);
}
public static IReturnsThrows<TMock, TReturn> OutCallback<TMock, TReturn, T1, TOut>(this ICallback<TMock, TReturn> mock, OutAction<T1, TOut> action)
where TMock : class
{
return OutCallbackInternal(mock, action);
}
private static IReturnsThrows<TMock, TReturn> OutCallbackInternal<TMock, TReturn>(ICallback<TMock, TReturn> mock, object action)
where TMock : class
{
mock.GetType()
.Assembly.GetType("Moq.MethodCall")
.InvokeMember("SetCallbackWithArguments", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, mock,
new[] { action });
return mock as IReturnsThrows<TMock, TReturn>;
}
}
Dengan metode ekstensi di atas, Anda dapat menguji antarmuka tanpa parameter seperti:
public interface IParser
{
bool TryParse(string token, out int value);
}
.. dengan pengaturan Moq berikut:
[TestMethod]
public void ParserTest()
{
Mock<IParser> parserMock = new Mock<IParser>();
int outVal;
parserMock
.Setup(p => p.TryParse("6", out outVal))
.OutCallback((string t, out int v) => v = 6)
.Returns(true);
int actualValue;
bool ret = parserMock.Object.TryParse("6", out actualValue);
Assert.IsTrue(ret);
Assert.AreEqual(6, actualValue);
}
Sunting : Untuk mendukung metode pengembalian kosong, Anda hanya perlu menambahkan metode kelebihan muatan baru:
public static ICallbackResult OutCallback<TOut>(this ICallback mock, OutAction<TOut> action)
{
return OutCallbackInternal(mock, action);
}
public static ICallbackResult OutCallback<T1, TOut>(this ICallback mock, OutAction<T1, TOut> action)
{
return OutCallbackInternal(mock, action);
}
private static ICallbackResult OutCallbackInternal(ICallback mock, object action)
{
mock.GetType().Assembly.GetType("Moq.MethodCall")
.InvokeMember("SetCallbackWithArguments", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, mock, new[] { action });
return (ICallbackResult)mock;
}
Ini memungkinkan pengujian antarmuka seperti:
public interface IValidationRule
{
void Validate(string input, out string message);
}
[TestMethod]
public void ValidatorTest()
{
Mock<IValidationRule> validatorMock = new Mock<IValidationRule>();
string outMessage;
validatorMock
.Setup(v => v.Validate("input", out outMessage))
.OutCallback((string i, out string m) => m = "success");
string actualMessage;
validatorMock.Object.Validate("input", out actualMessage);
Assert.AreEqual("success", actualMessage);
}
It.IsAny<T>()
pencocokan seperti (ref It.Ref<T>.IsAny
) hingga dukungan untuk pengaturan.Callback()
dan.Returns()
melalui tipe delegasi khusus yang cocok dengan tanda tangan metode. Metode yang dilindungi sama-sama didukung. Lihat misalnya jawaban saya di bawah ini .