Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/321.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 带有操作/函数参数的接口的Moq设置_C#_Unit Testing_Moq - Fatal编程技术网

C# 带有操作/函数参数的接口的Moq设置

C# 带有操作/函数参数的接口的Moq设置,c#,unit-testing,moq,C#,Unit Testing,Moq,我试图模拟对服务器的调用,并验证测试代码调用了正确的方法。代码结构如下: public interface IServerAdapter { void CallServer(Action<IServerExposingToClient> methodToCall, out bool serverCalled); object CallServer(Func<IServerExposingToClient, object> methodToCall, out

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

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);
}
现在的问题是测试和模拟。我需要声明已经在服务器上调用了方法(
AddResource
)。
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.");
调用
IsAddResource
时,函数不引用AddResource,只引用它的创建位置和调用参数

有人知道如何检查调用了哪个方法吗

对于名为的
out服务器的另一个问题,您可以认为
void
方法可以是
bool
,如果我们没有与服务器的连接,另一个方法可能返回
null
(最后一个参数不明确,因为null可能表示对象不存在,或者服务器不可用)

我非常想得到解决这两个问题的建议


提前感谢

问题一:

正如您所指出的,这里的回调是理想的。不幸的是,因为您的方法签名有一个out参数,所以Moq目前无法拦截它。有一个类似于您所关心的问题。没有迹象表明它将被解决

如果你想改变你的方法签名,考虑删除out参数——不管怎么说,它们都有点味道。如果在服务器不可用时抛出异常,事情就会简单得多。 问题二:

考虑将方法签名更改为表达式。我知道这里有很多尖括号,但是表达式在语法上等同于操作(您不必更改调用代码),并且允许回调遍历代码表达式树并获取调用方法的名称

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

public string GetMethodName(Expression<Action<IServerExposingToClient>> exp)
{
    return ((ExpressionMethodCall)exp.Body).Method.Name;
}
公共字符串GetMethodName(表达式exp)
{
return((ExpressionMethodCall)exp.Body).Method.Name;
}
现在,您已经有了足够的兵工厂来进行模拟。您可以编写一个回调来记录方法调用的名称,也可以编写匹配器来帮助定义调用方法时的行为

例如:

[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");
}
[测试]
加载设备模型时公共无效\u系统\u应记录OneNewResource()
{
//安排
var serverAdapterMock=new Mock();
var subject=新SubjectUnderTest(serverAdapterMock.Object);
//表演
subject.Initialize();
//断言
Verify(c=>c.CallServer(IsAddResource()),Times.justice(1));
}
静态表达式isAddressSource()
{
返回匹配。创建(
exp=>GetMethodName(exp)==“AddResource”);
}

我有一种感觉,我无法找出调用哪个方法的原因是编译后的代码在一个匿名委托中调用了AddResource,我(可能还有Moq)无法访问的内容以了解它的功能…+1。我同意;除了在Try Parse模式中,我不会使用out参数。异常,而不是错误标志/代码,是传递错误条件的首选方式。问题二:刚刚尝试过,它似乎工作得很好。在我的测试中,我将方法调用的签名更改为接受一个表达式,并删除out参数,并将其替换为方法接受操作的返回值。我必须看看项目负责人的想法。然后我们必须更改以前经过良好测试的(而不是单元测试)在发布前编写代码。特别是因为此更改是为测试而设计的,而不是为测试而设计的。我不喜欢使用参数,也不会自己选择。问题一:我不希望方法抛出异常。这将导致处理此问题的额外代码负载。它“更漂亮”(缺少更好的表达方式)而不是try/catch块,并且日志记录是在CallServer方法内部执行的。有没有更好的方法来解决这个问题而不是更改代码?编写单元测试的另一种方法来测试调用的方法?只需要在不接受更改的情况下解决问题。这个解决方案可能会导致问题吗?与我的旧方法相比,会出现什么问题?性能损失(尽管我认为用户不会注意到)?Verify+Match的完美使用。我已经搜索了很久了。
public string GetMethodName(Expression<Action<IServerExposingToClient>> exp)
{
    return ((ExpressionMethodCall)exp.Body).Method.Name;
}
[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");
}