Testing Moq-是否可以在设置中指定验证标准(例如调用的次数)?

Testing Moq-是否可以在设置中指定验证标准(例如调用的次数)?,testing,mocking,installation,moq,verify,Testing,Mocking,Installation,Moq,Verify,如果需要设置返回值,以及验证表达式被调用了多少次,可以在一条语句中完成吗 据我所知,Moq的设置(SomeExpression).Verifiable()与Verify()一起调用,基本上执行验证(SomeExpression,Times.atleastone)?i、 它验证表达式是否仅被调用 这里有一个例子可以更好地解释这个问题。对于接口: interface IFoo { int ReturnSomething(); } 以下两个模块是否等效(第一个模块除外)将验证标记为可验证的所

如果需要设置返回值,以及验证表达式被调用了多少次,可以在一条语句中完成吗

据我所知,Moq的
设置(SomeExpression).Verifiable()
Verify()
一起调用,基本上执行
验证(SomeExpression,Times.atleastone)
?i、 它验证表达式是否仅被调用

这里有一个例子可以更好地解释这个问题。对于接口:

interface IFoo
{
    int ReturnSomething();
}
以下两个模块是否等效(第一个模块除外)将验证标记为可验证的所有设置

void测试()
{
var mock=new mock();
mock.Setup((m)=>m.ReturnSomething()).Returns(1.Verifiable();
mock.Verify();
}

void测试()
{
var mock=new mock();
mock.Setup((m)=>m.ReturnSomething()).Returns(1);
mock.Verify((m)=>m.ReturnSomething(),Times.atleastone());
}
如果我想验证调用的数量(比如两次),这是唯一的方法吗?在这种方法中,表达式会重复用于设置和验证

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

    mock.Verify((m) => m.ReturnSomething(), Times.Exactly(2));
}
void测试()
{
var mock=new mock();
mock.Setup((m)=>m.ReturnSomething()).Returns(1);
mock.Verify((m)=>m.ReturnSomething(),Times.Verify(2));
}
我只是不喜欢打电话给安装和验证。既然这对AAA来说是个好主意,换句话来说,我不想在设置和验证时重复这个表达式。目前,我将表达式存储在一个变量中,并将其传递给每个方法,但感觉不太干净


PS-此上下文用于测试检查缓存是否更新(过期等)

要回答第一个问题,是的,这两个块是等效的。当调用
时,这两种方法都将失败。如果未调用模拟上的方法,请验证调用了

就我所知,您不能预先指定verify,如果您考虑一下,它是有意义的

这是在指定模拟的行为:

mock.Setup(m => m.ReturnSomething()).Returns(1);
这是在验证调用方的行为:

mock.Verify(m => m.ReturnSomething(), Times.AtLeastOnce());
就我个人而言,我更喜欢单独调用verify来确认调用方所需的行为,
.verifyable()
.verify()
是不太严格的快捷方式(它们只检查方法被调用了一次或多次),但是如果您知道您的代码应该只调用一次方法,将验证放在最后以确认它


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

我一直都有这个问题。我使用严格的模拟,我希望严格地指定(即我使用
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);
Expression-Expression=mockedTypeInstance=>mockedTypeInstance.MockedMethod(It.Is(firstArgument=>)/*,…*/);
_模拟设置(表达式);
/*运行测试*/
_模拟验证(表达式、次数、一次);

虽然可读性不强,但我认为没有其他方法可以同时使用严格的设置和严格的验证。

在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;
}

我创建了一个实用程序类来处理这个问题:

public class TestUtils
{
  private static List<Action> verifyActions = new List<Action>();

  public static void InitVerifyActions() => verifyActions = new List<Action>();

  public static void VerifyAllSetups()
  {
    foreach (var action in verifyActions)
    {
      action.Invoke();
    }
  }

  public static ISetup<T> SetupAndVerify<T>(Mock<T> mock, Expression<Action<T>> expression, Times times) where T : class
  {
    verifyActions.Add(() => mock.Verify(expression, times));
    return mock.Setup(expression);
  }

  public static ISetup<T, TResult> SetupAndVerify<T, TResult>(Mock<T> mock, Expression<Func<T, TResult>> expression, Times times) where T : class
  {
    verifyActions.Add(() => mock.Verify(expression, times));
    return mock.Setup(expression);
  }
}

考虑到Rhino Mock支持在单个语句中设置和验证,这是使用Moq 4(我见过旧的代码片段,其中ISetup上有AtMostOnce之类的方法)很不幸的。我认为这将是最常见的用例之一(如果不是最常见的话),所以我很困惑,即使是现在,在三年之后,它是如何没有包含在框架中的。我想我会切换到Rhino Mocks…使用一个单独的表达式来重复使用非常符合我的需要。很好的解决方案!好主意,但是如果我想让安装程序返回一个值,如何使用它?@ishayle就像你通常做的那样,对moq使用流畅的语法,例如_mock.setup(expression)。返回(…)我们还需要在较新版本的moq中指定安装和验证(用于断言时间)吗?或者有什么改变或不同的方法吗?
/// <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;
}
var createMapperCall = mockMappingFactory.CallTo(x => x.CreateMapper());
mockMappingFactory.Setup(createMapperCall).Returns(mockMapper.Object);

mockMappingFactory.Verify(createMapperCall, Times.Once());
public class TestUtils
{
  private static List<Action> verifyActions = new List<Action>();

  public static void InitVerifyActions() => verifyActions = new List<Action>();

  public static void VerifyAllSetups()
  {
    foreach (var action in verifyActions)
    {
      action.Invoke();
    }
  }

  public static ISetup<T> SetupAndVerify<T>(Mock<T> mock, Expression<Action<T>> expression, Times times) where T : class
  {
    verifyActions.Add(() => mock.Verify(expression, times));
    return mock.Setup(expression);
  }

  public static ISetup<T, TResult> SetupAndVerify<T, TResult>(Mock<T> mock, Expression<Func<T, TResult>> expression, Times times) where T : class
  {
    verifyActions.Add(() => mock.Verify(expression, times));
    return mock.Setup(expression);
  }
}
TestUtils.SetupAndVerify(myMock, m => m.Foo("bar"), Times.Once()).Returns("baz");
TestUtils.SetupAndVerify(myOtherMock, m => m.Blah(), Times.Once());
...
TestUtils.VerifyAllSetups();