Представьте себе этот класс
public class Foo {
private Handler _h;
public Foo(Handler h)
{
_h = h;
}
public void Bar(int i)
{
_h.AsyncHandle(CalcOn(i));
}
private SomeResponse CalcOn(int i)
{
...;
}
}
Mo (q) cking Обработчик в тесте Foo, как бы я мог проверить, что Bar()
перешло на _h.AsyncHandle
?
Ответ 1
Вы можете использовать метод Mock.Callback:
var mock = new Mock<Handler>();
SomeResponse result = null;
mock.Setup(h => h.AnsyncHandle(It.IsAny<SomeResponse>()))
.Callback<SomeResponse>(r => result = r);
// do your test
new Foo(mock.Object).Bar(22);
Assert.NotNull(result);
Если вы только хотите проверить что-то простое в аргументе, переданном в аргументе, вы также можете сделать это напрямую:
mock.Setup(h => h.AnsyncHandle(It.Is<SomeResponse>(response => response != null)));
Ответ 2
Ответ на Gamlor работает, но другой способ сделать это (и тот, который я считаю более выразительным в тесте)...
var mock = new Mock<Handler>();
var desiredParam = 47; // this is what you want to be passed to AsyncHandle
new Foo(mock.Object).Bar(22);
mock.Verify(h => h.AsyncHandle(desiredParam), Times.Once());
Verify очень мощный, и стоит потратить время, чтобы привыкнуть.
Ответ 3
Ответ Gamlor сработал для меня, но я подумал, что расширю комментарий Джона Карпентера, потому что я искал решение, включающее более одного параметра. Я полагал, что другие люди, которые натыкаются на эту страницу, могут быть в подобной ситуации. Я нашел эту информацию в документации Moq.
Я буду использовать пример Gamlor, но давайте представим, что метод AsyncHandle принимает два аргумента: string
и объект SomeResponse
.
var mock = new Mock<Handler>();
string stringResult = string.Empty;
SomeResponse someResponse = null;
mock.Setup(h => h.AsyncHandle(It.IsAny<string>(), It.IsAny<SomeResponse>()))
.Callback<string, SomeResponse>((s, r) =>
{
stringResult = s;
someResponse = r;
});
// do your test
new Foo(mock.Object).Bar(22);
Assert.AreEqual("expected string", stringResult);
Assert.IsNotNull(someResponse);
По сути, вам просто нужно добавить еще один It.IsAny<>()
с соответствующим типом, добавить другой тип в метод Callback
и изменить лямбда-выражение соответствующим образом.
Ответ 4
Метод Callback, безусловно, будет работать, но если вы делаете это для метода с большим количеством параметров, он может быть немного многословным. Вот кое-что, что я использовал, чтобы удалить часть шаблона.
var mock = new Mock<Handler>();
// do your test
new Foo(mock.Object).Bar(22);
var arg = new ArgumentCaptor<SomeResponse>();
mock.Verify(h => h.AsyncHandle(arg.Capture()));
Assert.NotNull(arg.Value);
Вот источник для ArgumentCaptor:
public class ArgumentCaptor<T>
{
public T Capture()
{
return It.Is<T>(t => SaveValue(t));
}
private bool SaveValue(T t)
{
Value = t;
return true;
}
public T Value { get; private set; }
}
Ответ 5
Вы можете использовать It.Is<TValue>()
matcher.
var mock = new Mock<Handler>();
new Foo(mock.Object).Bar(22);
mock.Verify(h => h.AsyncHandle(It.Is<SomeResponse>(r => r != null )));
Ответ 6
Альтернативой также является использование функции Capture.In
в moq
. Это функция OOTB moq
которая позволяет захватывать аргументы в коллекции.
//Arrange
var args = new List<SomeResponse>();
mock.Setup(h => h.AnsyncHandle(Capture.In(args)));
//Act
new Foo(mock.Object).Bar(22);
//Assert
//... assert args.Single() or args.First()
Ответ 7
Это также работает:
Mock<InterfaceThing> mockedObject = new Mock<InterfaceThing>();
var objectParameter = mockedObject.Invocations[1].Arguments[0] as ObjectParameter;
Ответ 8
Здесь много хороших ответов! Идите с готовым набором функций Moq, пока вам не понадобится сделать утверждения о нескольких параметрах класса, передаваемых вашим зависимостям. Однако, если вы окажетесь в такой ситуации, функция Moq Verify с It.Is matchers не поможет изолировать неудачу теста, а способ захвата аргументов Returns/Callback добавляет ненужные строки кода в ваш тест (и длинные тесты мне не нужны).
Вот суть: https://gist.github.com/Jacob-McKay/8b8d41ebb9565f5fca23654fd944ac6b с расширением Moq (4.12), которое я написал, которое дает более декларативный способ сделать утверждения об аргументах, передаваемых в макеты, без вышеупомянутых недостатков. Вот как выглядит раздел "Проверка":
mockDependency
.CheckMethodWasCalledOnce(nameof(IExampleDependency.PersistThings))
.WithArg<InThing2>(inThing2 =>
{
Assert.Equal("Input Data with Important additional data", inThing2.Prop1);
Assert.Equal("I need a trim", inThing2.Prop2);
})
.AndArg<InThing3>(inThing3 =>
{
Assert.Equal("Important Default Value", inThing3.Prop1);
Assert.Equal("I NEED TO BE UPPER CASED", inThing3.Prop2);
});
Я был бы рад, если бы Moq предоставил функцию, которая выполняет то же самое, в то же время являясь декларативной и обеспечивая изоляцию ошибок, что и делает. Скрещенные пальцы!