EDIT : Trong Moq 4.10, giờ đây bạn có thể chuyển một đại biểu có tham số out hoặc ref trực tiếp cho hàm Callback:
mock
.Setup(x=>x.Method(out d))
.Callback(myDelegate)
.Returns(...);
Bạn sẽ phải xác định một đại biểu và khởi tạo nó:
...
.Callback(new MyDelegate((out decimal v)=>v=12m))
...
Đối với phiên bản Moq trước 4.10:
Avner Kashtan cung cấp một phương thức mở rộng trong blog của mình, cho phép thiết lập tham số out từ một cuộc gọi lại: tham số Moq, Callbacks và Out: trường hợp cạnh đặc biệt khó khăn
Giải pháp là cả thanh lịch và hacky. Thanh lịch ở chỗ nó cung cấp một cú pháp trôi chảy mà cảm thấy như ở nhà với các cuộc gọi lại Moq khác. Và hacky vì nó dựa vào việc gọi một số API Moq nội bộ thông qua sự phản chiếu.
Phương thức mở rộng được cung cấp tại liên kết trên không biên dịch cho tôi, vì vậy tôi đã cung cấp phiên bản chỉnh sửa bên dưới. Bạn sẽ cần tạo một chữ ký cho mỗi số lượng tham số đầu vào bạn có; Tôi đã cung cấp 0 và 1, nhưng việc mở rộng hơn nữa nên đơn giản:
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>;
}
}
Với phương pháp mở rộng ở trên, bạn có thể kiểm tra một giao diện với các tham số như:
public interface IParser
{
bool TryParse(string token, out int value);
}
.. với thiết lập Moq sau:
[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);
}
Chỉnh sửa : Để hỗ trợ các phương thức void-return, bạn chỉ cần thêm các phương thức quá tải mới:
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;
}
Điều này cho phép thử nghiệm các giao diện như:
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>()
giống (ref It.Ref<T>.IsAny
) đến hỗ trợ để thiết lập.Callback()
và.Returns()
thông qua một loại đại biểu tùy chỉnh phù hợp với chữ ký phương thức. Phương pháp bảo vệ được hỗ trợ như nhau. Xem ví dụ câu trả lời của tôi dưới đây .