具有动作/函数参数的接口的Moq设置

big*_*oot 4 c# unit-testing moq

我试图模拟对服务器的调用,并验证测试的代码调用正确的方法.代码结构如下:

public interface IServerAdapter
{
    void CallServer(Action<IServerExposingToClient> methodToCall, out bool serverCalled);
    object CallServer(Func<IServerExposingToClient, object> methodToCall, out bool serverCalled);
}

public interface IServerExposingToClient
{
    Resource GetResource(string id);
    void AddResource(Resource resource);
}
Run Code Online (Sandbox Code Playgroud)

我正在测试的代码访问服务器上的一个实现IServerAdapter并调用IServerExposingToClient方法.这样做的原因是我们不希望在类中实现每个方法(它们中有很多方法)IServerExposingToClient.由于这是在代理上调用的,因此它在我们的代码中运行良好.

在服务器上调用方法是这样的:

_mainViewModel.ServerAdapter.CallServer(m => m.AddResource(resource), out serverCalled);
Run Code Online (Sandbox Code Playgroud)

现在的问题是测试和嘲笑.我需要断言AddResource在服务器上调用了method().在out serverCalled(问题1),以确保这样的逻辑,因为它应该呼叫者流动电话做了它对服务器.

当我使用以下Moq设置时,我可以断言在服务器上调用了一些方法:

Mock<IServerAdapter> serverAdapterMock = new Mock<IServerAdapter>();
bool serverCalled;
bool someMethodCalled = false;
serverAdapterMock.Setup(c => c.CallServer(It.IsAny<Action<IServerExposingToClient>>(), out serverCalled)).Callback(() => someMethodCalled = true);
// assign serverAdapterMock.Object to some property my code will use
// test code
Assert.IsTrue(someMethodCalled, "Should have called method on server.");
Run Code Online (Sandbox Code Playgroud)

正如测试所说,我可以断言在服务器上调用了一些方法.问题是我不知道调用了哪种方法.在某些方法中,我想要测试的逻辑可以根据条件采用不同的路径,对于某些情况,多个路径可能会导致服务器调用.因此,我需要知道调用哪个方法,以便在测试中得到正确的断言(问题2).

serverCalled需要将变量设置为匹配IServerAdapter mock上方法调用的签名.我也非常希望能够在一些回调中设置该变量,或者让我正在测试的代码的逻辑以它应该的方式流动的任何东西(问题1).由于它现在的工作方式,因此不会通过模拟设置来设置serverCalled.

主要问题是不知道针对服务器调用了哪种方法.我曾尝试使用Match来检查委托方法的名称,但没有使用sigar.

serverAdapterMock.Setup(c => c.CallServer(Match.Create<Action<IServerExposingToClient>>( x => IsAddResource(x)), out serverCalled)).Callback(() => someMethodCalled = true);
Run Code Online (Sandbox Code Playgroud)

IsAddResource被调用时,该功能并不是指AddResource,只有在创建它,什么说法它被称为用.

有谁知道如何检查调用哪种方法?

对于另一个问题out serverCalled,您可以争辩说该void方法可以bool改为,并且null如果我们没有与服务器的连接,则另一个方法可以返回(最后一个参数将是不明确的,因为null可能意味着对象不存在,或服务器不可用).

我非常希望能找到解决这两个问题的建议.

提前致谢

bry*_*ook 6

问题一:

正如您所指出的,这里的回调将是理想的.不幸的是,因为你的方法签名有一个out参数,所以它目前不能被Moq拦截.Moq论坛中有一篇与您关注的内容类似的帖子.没有任何迹象表明它会得到解决.

如果您愿意更改方法签名,请考虑删除out参数 - 无论如何它们都有点气味.如果您在服务器不可用时抛出异常,事情就会简单得多.获得该的有(双关语意)会打开你的回调来处理你的第二个问题.

问题二:

考虑将方法签名更改为Expression <Action <IServerExposingToClient >>.我意识到那里有很多尖括号,但是Expression在语法上等同于Action <T>(你不必更改你的调用代码)并允许你的Callback遍历代码表达式树并得到你的名字调用方法.

您可以使用以下内容获取方法的名称:

public string GetMethodName(Expression<Action<IServerExposingToClient>> exp)
{
    return ((ExpressionMethodCall)exp.Body).Method.Name;
}
Run Code Online (Sandbox Code Playgroud)

你现在有足够的武器库去追求你的模拟.您可以编写一个记录方法调用名称的回调,也可以编写匹配器来帮助定义调用方法时的行为.

例如:

[Test]
public void WhenTheViewModelIsLoaded_TheSystem_ShouldRecordOneNewResource()
{
   // arrange
   var serverAdapterMock = new Mock<IServerAdapter>();
   var subject = new SubjectUnderTest( serverAdapterMock.Object );

   // act
   subject.Initialize();

   // assert
   serverAdapterMock.Verify( c => c.CallServer( IsAddResource() ), Times.Exactly(1));
}

static Expression<Action<IServerExposedToClient>> IsAddResource()
{
    return Match.Create<Expression<Action<IServerExposedToClient>>>(
              exp => GetMethodName(exp) == "AddResource");
}
Run Code Online (Sandbox Code Playgroud)

  • +1.我同意; 除了在Try-Parse模式中,我不会使用out参数.例外,而不是错误标志/代码,是沟通错误条件的首选方式. (2认同)