Moq - 是否可以在设置中指定验证标准(例如,被称为时间)?

Gre*_*egS 35 testing installation moq mocking verify

如果需要设置返回值,以及验证调用表达式的次数,可以在一个语句中执行此操作吗?

从我可以收集到的,Moq的Setup(SomeExpression).Verifiable()调用Verify(),基本上是一个Verify(SomeExpression, Times.AtLeastOnce)?即它验证表达式只被调用.

这是一个更好地解释问题的例子.对于接口:

interface IFoo
{
    int ReturnSomething();
}
Run Code Online (Sandbox Code Playgroud)

以下两个块是否等效(除了第一个将验证标记为可验证的所有设置)?

void Test()
{
    var mock = new Mock<IFoo>();
    mock.Setup((m) => m.ReturnSomething()).Returns(1).Verifiable();

    mock.Verify();
}
Run Code Online (Sandbox Code Playgroud)

void Test()
{
    var mock = new Mock<IFoo>();
    mock.Setup((m) => m.ReturnSomething()).Returns(1);

    mock.Verify((m) => m.ReturnSomething(), Times.AtLeastOnce());
}
Run Code Online (Sandbox Code Playgroud)

如果我想验证调用次数(比如两次),这是唯一的方法,其中表达式重复进行安装和验证吗?

void Test()
{
    var mock = new Mock<IFoo>();
    mock.Setup((m) => m.ReturnSomething()).Returns(1);

    mock.Verify((m) => m.ReturnSomething(), Times.Exactly(2));
}
Run Code Online (Sandbox Code Playgroud)

我只是不喜欢打电话给安装和验证.好吧,因为这对AAA来说是一个好主意,换句话说,我不喜欢重复设置和验证的表达式.目前我将表达式存储在变量中并将其传递给每个方法,但感觉不是那么干净.

PS - 上下文用于检查何时更新缓存(explirations等)的测试

Tre*_*ley 16

要回答第一个问题,是的,这两个块是等价的..Verify如果未调用mock上的方法,则两者都将失败.

据我所知,您无法预先指定验证,如果您考虑它,它是有道理的.

这是指定模拟的行为:

mock.Setup(m => m.ReturnSomething()).Returns(1);
Run Code Online (Sandbox Code Playgroud)

这是验证调用者的行为:

mock.Verify(m => m.ReturnSomething(), Times.AtLeastOnce());
Run Code Online (Sandbox Code Playgroud)

我个人更喜欢单独调用验证以确认调用者所需的行为,.Verifiable()并且.Verify()是不太严格的快捷方式(他们只是检查方法被调用了一次或多次)但是如果你知道你的代码应该只调用一次方法,那么最后验证确认.

我开始这样做之后,代码合并导致一个方法被调用两次,测试仍然通过,因为它被调用至少一次,但它也意味着其他事情多次发生,不应该有!

  • 考虑到Rhino Mocks支持在单个语句中进行设置和验证,这很不幸。我认为这将是最常见的用例之一(如果不是最常见的用例),因此我困惑于如何在三年后的今天仍不将其包括在框架中。猜猜我会改用Rhino Mocks ... (3认同)

Evr*_*glu 15

我一直都有这个问题.我使用严格的模拟,我想严格指定(即我使用It.Is<>()而不是It.IsAny())以及严格验证(即指定时间).你不能遗憾地使用可验证的,因为Moq缺少Verifiable(Times)过载.

电话的完整表达,包括It.Is<>()一般都很大.因此,为了避免重复,我通常采用以下方法:

Expression<Action<MockedType>> expression = mockedTypeInstance => mockedTypeInstance.MockedMethod(It.Is<TFirstArgument>(firstArgument => <some complex statement>)/*, ...*/);
_mock.Setup(expression);

/* run the test*/

_mock.Verify(expression, Times.Once);
Run Code Online (Sandbox Code Playgroud)

不是非常易读,但我认为还没有其他方法可以使用严格的设置和严格的验证.

  • 使用可重复使用的单独表达式非常适合我的需要。很好的解决方案! (2认同)

Jac*_* A. 5

在解释Evren Kuzucuoglu的答案时,我创建了以下扩展方法以使表达式的创建更加简单:

/// <summary>
/// Creates a method call expression that can be passed to both <see cref="Setup"/> and <see cref="Verify"/>.
/// </summary>
/// <typeparam name="T">Mocked object type.</typeparam>
/// <param name="mock">Mock of <see cref="T"/>.</param>
/// <param name="expression">Method call expression to record.</param>
/// <returns>Method call expression.</returns>
public static Expression<Action<T>> CallTo<T>(this Mock<T> mock, Expression<Action<T>> expression) where T : class
{
    return expression;
}

/// <summary>
/// Creates a method call expression that can be passed to both <see cref="Setup"/> and <see cref="Verify"/>.
/// </summary>
/// <typeparam name="T">Mocked object type.</typeparam>
/// <typeparam name="TResult">Method call return type.</typeparam>
/// <param name="mock">Mock of <see cref="T"/>.</param>
/// <param name="expression">Method call expression to record.</param>
/// <returns>Method call expression.</returns>
public static Expression<Func<T, TResult>> CallTo<T, TResult>(this Mock<T> mock, Expression<Func<T, TResult>> expression) where T : class
{
    return expression;
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

var createMapperCall = mockMappingFactory.CallTo(x => x.CreateMapper());
mockMappingFactory.Setup(createMapperCall).Returns(mockMapper.Object);

mockMappingFactory.Verify(createMapperCall, Times.Once());
Run Code Online (Sandbox Code Playgroud)